Roo/bootstrap/version.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @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)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
921     setWeight : function(str)
922     {
923         this.el.removeClass(this.weightClass);
924         this.el.addClass('btn-' + str);        
925     }
926     
927     
928 });
929
930  /*
931  * - LGPL
932  *
933  * column
934  * 
935  */
936
937 /**
938  * @class Roo.bootstrap.Column
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Column class
941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
949  *
950  * 
951  * @cfg {Boolean} hidden (true|false) hide the element
952  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953  * @cfg {String} fa (ban|check|...) font awesome icon
954  * @cfg {Number} fasize (1|2|....) font awsome size
955
956  * @cfg {String} icon (info-sign|check|...) glyphicon name
957
958  * @cfg {String} html content of column.
959  * 
960  * @constructor
961  * Create a new Column
962  * @param {Object} config The config object
963  */
964
965 Roo.bootstrap.Column = function(config){
966     Roo.bootstrap.Column.superclass.constructor.call(this, config);
967 };
968
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
970     
971     xs: false,
972     sm: false,
973     md: false,
974     lg: false,
975     xsoff: false,
976     smoff: false,
977     mdoff: false,
978     lgoff: false,
979     html: '',
980     offset: 0,
981     alert: false,
982     fa: false,
983     icon : false,
984     hidden : false,
985     fasize : 1,
986     
987     getAutoCreate : function(){
988         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
989         
990         cfg = {
991             tag: 'div',
992             cls: 'column'
993         };
994         
995         var settings=this;
996         ['xs','sm','md','lg'].map(function(size){
997             //Roo.log( size + ':' + settings[size]);
998             
999             if (settings[size+'off'] !== false) {
1000                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1001             }
1002             
1003             if (settings[size] === false) {
1004                 return;
1005             }
1006             
1007             if (!settings[size]) { // 0 = hidden
1008                 cfg.cls += ' hidden-' + size;
1009                 return;
1010             }
1011             cfg.cls += ' col-' + size + '-' + settings[size];
1012             
1013         });
1014         
1015         if (this.hidden) {
1016             cfg.cls += ' hidden';
1017         }
1018         
1019         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020             cfg.cls +=' alert alert-' + this.alert;
1021         }
1022         
1023         
1024         if (this.html.length) {
1025             cfg.html = this.html;
1026         }
1027         if (this.fa) {
1028             var fasize = '';
1029             if (this.fasize > 1) {
1030                 fasize = ' fa-' + this.fasize + 'x';
1031             }
1032             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1033             
1034             
1035         }
1036         if (this.icon) {
1037             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1038         }
1039         
1040         return cfg;
1041     }
1042    
1043 });
1044
1045  
1046
1047  /*
1048  * - LGPL
1049  *
1050  * page container.
1051  * 
1052  */
1053
1054
1055 /**
1056  * @class Roo.bootstrap.Container
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Container class
1059  * @cfg {Boolean} jumbotron is it a jumbotron element
1060  * @cfg {String} html content of element
1061  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1063  * @cfg {String} header content of header (for panel)
1064  * @cfg {String} footer content of footer (for panel)
1065  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066  * @cfg {String} tag (header|aside|section) type of HTML tag.
1067  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068  * @cfg {String} fa font awesome icon
1069  * @cfg {String} icon (info-sign|check|...) glyphicon name
1070  * @cfg {Boolean} hidden (true|false) hide the element
1071  * @cfg {Boolean} expandable (true|false) default false
1072  * @cfg {Boolean} expanded (true|false) default true
1073  * @cfg {String} rheader contet on the right of header
1074  * @cfg {Boolean} clickable (true|false) default false
1075
1076  *     
1077  * @constructor
1078  * Create a new Container
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Container = function(config){
1083     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // raw events
1087          /**
1088          * @event expand
1089          * After the panel has been expand
1090          * 
1091          * @param {Roo.bootstrap.Container} this
1092          */
1093         "expand" : true,
1094         /**
1095          * @event collapse
1096          * After the panel has been collapsed
1097          * 
1098          * @param {Roo.bootstrap.Container} this
1099          */
1100         "collapse" : true,
1101         /**
1102          * @event click
1103          * When a element is chick
1104          * @param {Roo.bootstrap.Container} this
1105          * @param {Roo.EventObject} e
1106          */
1107         "click" : true
1108     });
1109 };
1110
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1112     
1113     jumbotron : false,
1114     well: '',
1115     panel : '',
1116     header: '',
1117     footer : '',
1118     sticky: '',
1119     tag : false,
1120     alert : false,
1121     fa: false,
1122     icon : false,
1123     expandable : false,
1124     rheader : '',
1125     expanded : true,
1126     clickable: false,
1127   
1128      
1129     getChildContainer : function() {
1130         
1131         if(!this.el){
1132             return false;
1133         }
1134         
1135         if (this.panel.length) {
1136             return this.el.select('.panel-body',true).first();
1137         }
1138         
1139         return this.el;
1140     },
1141     
1142     
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag : this.tag || 'div',
1147             html : '',
1148             cls : ''
1149         };
1150         if (this.jumbotron) {
1151             cfg.cls = 'jumbotron';
1152         }
1153         
1154         
1155         
1156         // - this is applied by the parent..
1157         //if (this.cls) {
1158         //    cfg.cls = this.cls + '';
1159         //}
1160         
1161         if (this.sticky.length) {
1162             
1163             var bd = Roo.get(document.body);
1164             if (!bd.hasClass('bootstrap-sticky')) {
1165                 bd.addClass('bootstrap-sticky');
1166                 Roo.select('html',true).setStyle('height', '100%');
1167             }
1168              
1169             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1170         }
1171         
1172         
1173         if (this.well.length) {
1174             switch (this.well) {
1175                 case 'lg':
1176                 case 'sm':
1177                     cfg.cls +=' well well-' +this.well;
1178                     break;
1179                 default:
1180                     cfg.cls +=' well';
1181                     break;
1182             }
1183         }
1184         
1185         if (this.hidden) {
1186             cfg.cls += ' hidden';
1187         }
1188         
1189         
1190         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191             cfg.cls +=' alert alert-' + this.alert;
1192         }
1193         
1194         var body = cfg;
1195         
1196         if (this.panel.length) {
1197             cfg.cls += ' panel panel-' + this.panel;
1198             cfg.cn = [];
1199             if (this.header.length) {
1200                 
1201                 var h = [];
1202                 
1203                 if(this.expandable){
1204                     
1205                     cfg.cls = cfg.cls + ' expandable';
1206                     
1207                     h.push({
1208                         tag: 'i',
1209                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1210                     });
1211                     
1212                 }
1213                 
1214                 h.push(
1215                     {
1216                         tag: 'span',
1217                         cls : 'panel-title',
1218                         html : (this.expandable ? '&nbsp;' : '') + this.header
1219                     },
1220                     {
1221                         tag: 'span',
1222                         cls: 'panel-header-right',
1223                         html: this.rheader
1224                     }
1225                 );
1226                 
1227                 cfg.cn.push({
1228                     cls : 'panel-heading',
1229                     style : this.expandable ? 'cursor: pointer' : '',
1230                     cn : h
1231                 });
1232                 
1233             }
1234             
1235             body = false;
1236             cfg.cn.push({
1237                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1238                 html : this.html
1239             });
1240             
1241             
1242             if (this.footer.length) {
1243                 cfg.cn.push({
1244                     cls : 'panel-footer',
1245                     html : this.footer
1246                     
1247                 });
1248             }
1249             
1250         }
1251         
1252         if (body) {
1253             body.html = this.html || cfg.html;
1254             // prefix with the icons..
1255             if (this.fa) {
1256                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1257             }
1258             if (this.icon) {
1259                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1260             }
1261             
1262             
1263         }
1264         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265             cfg.cls =  'container';
1266         }
1267         
1268         return cfg;
1269     },
1270     
1271     initEvents: function() 
1272     {
1273         if(this.expandable){
1274             var headerEl = this.headerEl();
1275         
1276             if(headerEl){
1277                 headerEl.on('click', this.onToggleClick, this);
1278             }
1279         }
1280         
1281         if(this.clickable){
1282             this.el.on('click', this.onClick, this);
1283         }
1284         
1285     },
1286     
1287     onToggleClick : function()
1288     {
1289         var headerEl = this.headerEl();
1290         
1291         if(!headerEl){
1292             return;
1293         }
1294         
1295         if(this.expanded){
1296             this.collapse();
1297             return;
1298         }
1299         
1300         this.expand();
1301     },
1302     
1303     expand : function()
1304     {
1305         if(this.fireEvent('expand', this)) {
1306             
1307             this.expanded = true;
1308             
1309             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1310             
1311             this.el.select('.panel-body',true).first().removeClass('hide');
1312             
1313             var toggleEl = this.toggleEl();
1314
1315             if(!toggleEl){
1316                 return;
1317             }
1318
1319             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1320         }
1321         
1322     },
1323     
1324     collapse : function()
1325     {
1326         if(this.fireEvent('collapse', this)) {
1327             
1328             this.expanded = false;
1329             
1330             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331             this.el.select('.panel-body',true).first().addClass('hide');
1332         
1333             var toggleEl = this.toggleEl();
1334
1335             if(!toggleEl){
1336                 return;
1337             }
1338
1339             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1340         }
1341     },
1342     
1343     toggleEl : function()
1344     {
1345         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1346             return;
1347         }
1348         
1349         return this.el.select('.panel-heading .fa',true).first();
1350     },
1351     
1352     headerEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading',true).first()
1359     },
1360     
1361     bodyEl : function()
1362     {
1363         if(!this.el || !this.panel.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-body',true).first()
1368     },
1369     
1370     titleEl : function()
1371     {
1372         if(!this.el || !this.panel.length || !this.header.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-title',true).first();
1377     },
1378     
1379     setTitle : function(v)
1380     {
1381         var titleEl = this.titleEl();
1382         
1383         if(!titleEl){
1384             return;
1385         }
1386         
1387         titleEl.dom.innerHTML = v;
1388     },
1389     
1390     getTitle : function()
1391     {
1392         
1393         var titleEl = this.titleEl();
1394         
1395         if(!titleEl){
1396             return '';
1397         }
1398         
1399         return titleEl.dom.innerHTML;
1400     },
1401     
1402     setRightTitle : function(v)
1403     {
1404         var t = this.el.select('.panel-header-right',true).first();
1405         
1406         if(!t){
1407             return;
1408         }
1409         
1410         t.dom.innerHTML = v;
1411     },
1412     
1413     onClick : function(e)
1414     {
1415         e.preventDefault();
1416         
1417         this.fireEvent('click', this, e);
1418     }
1419 });
1420
1421  /*
1422  * - LGPL
1423  *
1424  * image
1425  * 
1426  */
1427
1428
1429 /**
1430  * @class Roo.bootstrap.Img
1431  * @extends Roo.bootstrap.Component
1432  * Bootstrap Img class
1433  * @cfg {Boolean} imgResponsive false | true
1434  * @cfg {String} border rounded | circle | thumbnail
1435  * @cfg {String} src image source
1436  * @cfg {String} alt image alternative text
1437  * @cfg {String} href a tag href
1438  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439  * @cfg {String} xsUrl xs image source
1440  * @cfg {String} smUrl sm image source
1441  * @cfg {String} mdUrl md image source
1442  * @cfg {String} lgUrl lg image source
1443  * 
1444  * @constructor
1445  * Create a new Input
1446  * @param {Object} config The config object
1447  */
1448
1449 Roo.bootstrap.Img = function(config){
1450     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451     
1452     this.addEvents({
1453         // img events
1454         /**
1455          * @event click
1456          * The img click event for the img.
1457          * @param {Roo.EventObject} e
1458          */
1459         "click" : true
1460     });
1461 };
1462
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1464     
1465     imgResponsive: true,
1466     border: '',
1467     src: 'about:blank',
1468     href: false,
1469     target: false,
1470     xsUrl: '',
1471     smUrl: '',
1472     mdUrl: '',
1473     lgUrl: '',
1474
1475     getAutoCreate : function()
1476     {   
1477         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478             return this.createSingleImg();
1479         }
1480         
1481         var cfg = {
1482             tag: 'div',
1483             cls: 'roo-image-responsive-group',
1484             cn: []
1485         };
1486         var _this = this;
1487         
1488         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1489             
1490             if(!_this[size + 'Url']){
1491                 return;
1492             }
1493             
1494             var img = {
1495                 tag: 'img',
1496                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497                 html: _this.html || cfg.html,
1498                 src: _this[size + 'Url']
1499             };
1500             
1501             img.cls += ' roo-image-responsive-' + size;
1502             
1503             var s = ['xs', 'sm', 'md', 'lg'];
1504             
1505             s.splice(s.indexOf(size), 1);
1506             
1507             Roo.each(s, function(ss){
1508                 img.cls += ' hidden-' + ss;
1509             });
1510             
1511             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512                 cfg.cls += ' img-' + _this.border;
1513             }
1514             
1515             if(_this.alt){
1516                 cfg.alt = _this.alt;
1517             }
1518             
1519             if(_this.href){
1520                 var a = {
1521                     tag: 'a',
1522                     href: _this.href,
1523                     cn: [
1524                         img
1525                     ]
1526                 };
1527
1528                 if(this.target){
1529                     a.target = _this.target;
1530                 }
1531             }
1532             
1533             cfg.cn.push((_this.href) ? a : img);
1534             
1535         });
1536         
1537         return cfg;
1538     },
1539     
1540     createSingleImg : function()
1541     {
1542         var cfg = {
1543             tag: 'img',
1544             cls: (this.imgResponsive) ? 'img-responsive' : '',
1545             html : null,
1546             src : 'about:blank'  // just incase src get's set to undefined?!?
1547         };
1548         
1549         cfg.html = this.html || cfg.html;
1550         
1551         cfg.src = this.src || cfg.src;
1552         
1553         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554             cfg.cls += ' img-' + this.border;
1555         }
1556         
1557         if(this.alt){
1558             cfg.alt = this.alt;
1559         }
1560         
1561         if(this.href){
1562             var a = {
1563                 tag: 'a',
1564                 href: this.href,
1565                 cn: [
1566                     cfg
1567                 ]
1568             };
1569             
1570             if(this.target){
1571                 a.target = this.target;
1572             }
1573             
1574         }
1575         
1576         return (this.href) ? a : cfg;
1577     },
1578     
1579     initEvents: function() 
1580     {
1581         if(!this.href){
1582             this.el.on('click', this.onClick, this);
1583         }
1584         
1585     },
1586     
1587     onClick : function(e)
1588     {
1589         Roo.log('img onclick');
1590         this.fireEvent('click', this, e);
1591     },
1592     /**
1593      * Sets the url of the image - used to update it
1594      * @param {String} url the url of the image
1595      */
1596     
1597     setSrc : function(url)
1598     {
1599         this.src =  url;
1600         
1601         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602             this.el.dom.src =  url;
1603             return;
1604         }
1605         
1606         this.el.select('img', true).first().dom.src =  url;
1607     }
1608     
1609     
1610    
1611 });
1612
1613  /*
1614  * - LGPL
1615  *
1616  * image
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.Link
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap Link Class
1625  * @cfg {String} alt image alternative text
1626  * @cfg {String} href a tag href
1627  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628  * @cfg {String} html the content of the link.
1629  * @cfg {String} anchor name for the anchor link
1630  * @cfg {String} fa - favicon
1631
1632  * @cfg {Boolean} preventDefault (true | false) default false
1633
1634  * 
1635  * @constructor
1636  * Create a new Input
1637  * @param {Object} config The config object
1638  */
1639
1640 Roo.bootstrap.Link = function(config){
1641     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642     
1643     this.addEvents({
1644         // img events
1645         /**
1646          * @event click
1647          * The img click event for the img.
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1655     
1656     href: false,
1657     target: false,
1658     preventDefault: false,
1659     anchor : false,
1660     alt : false,
1661     fa: false,
1662
1663
1664     getAutoCreate : function()
1665     {
1666         var html = this.html || '';
1667         
1668         if (this.fa !== false) {
1669             html = '<i class="fa fa-' + this.fa + '"></i>';
1670         }
1671         var cfg = {
1672             tag: 'a'
1673         };
1674         // anchor's do not require html/href...
1675         if (this.anchor === false) {
1676             cfg.html = html;
1677             cfg.href = this.href || '#';
1678         } else {
1679             cfg.name = this.anchor;
1680             if (this.html !== false || this.fa !== false) {
1681                 cfg.html = html;
1682             }
1683             if (this.href !== false) {
1684                 cfg.href = this.href;
1685             }
1686         }
1687         
1688         if(this.alt !== false){
1689             cfg.alt = this.alt;
1690         }
1691         
1692         
1693         if(this.target !== false) {
1694             cfg.target = this.target;
1695         }
1696         
1697         return cfg;
1698     },
1699     
1700     initEvents: function() {
1701         
1702         if(!this.href || this.preventDefault){
1703             this.el.on('click', this.onClick, this);
1704         }
1705     },
1706     
1707     onClick : function(e)
1708     {
1709         if(this.preventDefault){
1710             e.preventDefault();
1711         }
1712         //Roo.log('img onclick');
1713         this.fireEvent('click', this, e);
1714     }
1715    
1716 });
1717
1718  /*
1719  * - LGPL
1720  *
1721  * header
1722  * 
1723  */
1724
1725 /**
1726  * @class Roo.bootstrap.Header
1727  * @extends Roo.bootstrap.Component
1728  * Bootstrap Header class
1729  * @cfg {String} html content of header
1730  * @cfg {Number} level (1|2|3|4|5|6) default 1
1731  * 
1732  * @constructor
1733  * Create a new Header
1734  * @param {Object} config The config object
1735  */
1736
1737
1738 Roo.bootstrap.Header  = function(config){
1739     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1740 };
1741
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1743     
1744     //href : false,
1745     html : false,
1746     level : 1,
1747     
1748     
1749     
1750     getAutoCreate : function(){
1751         
1752         
1753         
1754         var cfg = {
1755             tag: 'h' + (1 *this.level),
1756             html: this.html || ''
1757         } ;
1758         
1759         return cfg;
1760     }
1761    
1762 });
1763
1764  
1765
1766  /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776  
1777 /**
1778  * @class Roo.bootstrap.MenuMgr
1779  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1780  * @singleton
1781  */
1782 Roo.bootstrap.MenuMgr = function(){
1783    var menus, active, groups = {}, attached = false, lastShow = new Date();
1784
1785    // private - called when first menu is created
1786    function init(){
1787        menus = {};
1788        active = new Roo.util.MixedCollection();
1789        Roo.get(document).addKeyListener(27, function(){
1790            if(active.length > 0){
1791                hideAll();
1792            }
1793        });
1794    }
1795
1796    // private
1797    function hideAll(){
1798        if(active && active.length > 0){
1799            var c = active.clone();
1800            c.each(function(m){
1801                m.hide();
1802            });
1803        }
1804    }
1805
1806    // private
1807    function onHide(m){
1808        active.remove(m);
1809        if(active.length < 1){
1810            Roo.get(document).un("mouseup", onMouseDown);
1811             
1812            attached = false;
1813        }
1814    }
1815
1816    // private
1817    function onShow(m){
1818        var last = active.last();
1819        lastShow = new Date();
1820        active.add(m);
1821        if(!attached){
1822           Roo.get(document).on("mouseup", onMouseDown);
1823            
1824            attached = true;
1825        }
1826        if(m.parentMenu){
1827           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828           m.parentMenu.activeChild = m;
1829        }else if(last && last.isVisible()){
1830           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1831        }
1832    }
1833
1834    // private
1835    function onBeforeHide(m){
1836        if(m.activeChild){
1837            m.activeChild.hide();
1838        }
1839        if(m.autoHideTimer){
1840            clearTimeout(m.autoHideTimer);
1841            delete m.autoHideTimer;
1842        }
1843    }
1844
1845    // private
1846    function onBeforeShow(m){
1847        var pm = m.parentMenu;
1848        if(!pm && !m.allowOtherMenus){
1849            hideAll();
1850        }else if(pm && pm.activeChild && active != m){
1851            pm.activeChild.hide();
1852        }
1853    }
1854
1855    // private this should really trigger on mouseup..
1856    function onMouseDown(e){
1857         Roo.log("on Mouse Up");
1858         
1859         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860             Roo.log("MenuManager hideAll");
1861             hideAll();
1862             e.stopEvent();
1863         }
1864         
1865         
1866    }
1867
1868    // private
1869    function onBeforeCheck(mi, state){
1870        if(state){
1871            var g = groups[mi.group];
1872            for(var i = 0, l = g.length; i < l; i++){
1873                if(g[i] != mi){
1874                    g[i].setChecked(false);
1875                }
1876            }
1877        }
1878    }
1879
1880    return {
1881
1882        /**
1883         * Hides all menus that are currently visible
1884         */
1885        hideAll : function(){
1886             hideAll();  
1887        },
1888
1889        // private
1890        register : function(menu){
1891            if(!menus){
1892                init();
1893            }
1894            menus[menu.id] = menu;
1895            menu.on("beforehide", onBeforeHide);
1896            menu.on("hide", onHide);
1897            menu.on("beforeshow", onBeforeShow);
1898            menu.on("show", onShow);
1899            var g = menu.group;
1900            if(g && menu.events["checkchange"]){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menu);
1905                menu.on("checkchange", onCheck);
1906            }
1907        },
1908
1909         /**
1910          * Returns a {@link Roo.menu.Menu} object
1911          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912          * be used to generate and return a new Menu instance.
1913          */
1914        get : function(menu){
1915            if(typeof menu == "string"){ // menu id
1916                return menus[menu];
1917            }else if(menu.events){  // menu instance
1918                return menu;
1919            }
1920            /*else if(typeof menu.length == 'number'){ // array of menu items?
1921                return new Roo.bootstrap.Menu({items:menu});
1922            }else{ // otherwise, must be a config
1923                return new Roo.bootstrap.Menu(menu);
1924            }
1925            */
1926            return false;
1927        },
1928
1929        // private
1930        unregister : function(menu){
1931            delete menus[menu.id];
1932            menu.un("beforehide", onBeforeHide);
1933            menu.un("hide", onHide);
1934            menu.un("beforeshow", onBeforeShow);
1935            menu.un("show", onShow);
1936            var g = menu.group;
1937            if(g && menu.events["checkchange"]){
1938                groups[g].remove(menu);
1939                menu.un("checkchange", onCheck);
1940            }
1941        },
1942
1943        // private
1944        registerCheckable : function(menuItem){
1945            var g = menuItem.group;
1946            if(g){
1947                if(!groups[g]){
1948                    groups[g] = [];
1949                }
1950                groups[g].push(menuItem);
1951                menuItem.on("beforecheckchange", onBeforeCheck);
1952            }
1953        },
1954
1955        // private
1956        unregisterCheckable : function(menuItem){
1957            var g = menuItem.group;
1958            if(g){
1959                groups[g].remove(menuItem);
1960                menuItem.un("beforecheckchange", onBeforeCheck);
1961            }
1962        }
1963    };
1964 }();/*
1965  * - LGPL
1966  *
1967  * menu
1968  * 
1969  */
1970
1971 /**
1972  * @class Roo.bootstrap.Menu
1973  * @extends Roo.bootstrap.Component
1974  * Bootstrap Menu class - container for MenuItems
1975  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1977  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1978  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1979  * 
1980  * @constructor
1981  * Create a new Menu
1982  * @param {Object} config The config object
1983  */
1984
1985
1986 Roo.bootstrap.Menu = function(config){
1987     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988     if (this.registerMenu && this.type != 'treeview')  {
1989         Roo.bootstrap.MenuMgr.register(this);
1990     }
1991     this.addEvents({
1992         /**
1993          * @event beforeshow
1994          * Fires before this menu is displayed
1995          * @param {Roo.menu.Menu} this
1996          */
1997         beforeshow : true,
1998         /**
1999          * @event beforehide
2000          * Fires before this menu is hidden
2001          * @param {Roo.menu.Menu} this
2002          */
2003         beforehide : true,
2004         /**
2005          * @event show
2006          * Fires after this menu is displayed
2007          * @param {Roo.menu.Menu} this
2008          */
2009         show : true,
2010         /**
2011          * @event hide
2012          * Fires after this menu is hidden
2013          * @param {Roo.menu.Menu} this
2014          */
2015         hide : true,
2016         /**
2017          * @event click
2018          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021          * @param {Roo.EventObject} e
2022          */
2023         click : true,
2024         /**
2025          * @event mouseover
2026          * Fires when the mouse is hovering over this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseover : true,
2032         /**
2033          * @event mouseout
2034          * Fires when the mouse exits this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseout : true,
2040         /**
2041          * @event itemclick
2042          * Fires when a menu item contained in this menu is clicked
2043          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044          * @param {Roo.EventObject} e
2045          */
2046         itemclick: true
2047     });
2048     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2049 };
2050
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2052     
2053    /// html : false,
2054     //align : '',
2055     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2056     type: false,
2057     /**
2058      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2059      */
2060     registerMenu : true,
2061     
2062     menuItems :false, // stores the menu items..
2063     
2064     hidden:true,
2065         
2066     parentMenu : false,
2067     
2068     stopEvent : true,
2069     
2070     isLink : false,
2071     
2072     getChildContainer : function() {
2073         return this.el;  
2074     },
2075     
2076     getAutoCreate : function(){
2077          
2078         //if (['right'].indexOf(this.align)!==-1) {
2079         //    cfg.cn[1].cls += ' pull-right'
2080         //}
2081         
2082         
2083         var cfg = {
2084             tag : 'ul',
2085             cls : 'dropdown-menu' ,
2086             style : 'z-index:1000'
2087             
2088         };
2089         
2090         if (this.type === 'submenu') {
2091             cfg.cls = 'submenu active';
2092         }
2093         if (this.type === 'treeview') {
2094             cfg.cls = 'treeview-menu';
2095         }
2096         
2097         return cfg;
2098     },
2099     initEvents : function() {
2100         
2101        // Roo.log("ADD event");
2102        // Roo.log(this.triggerEl.dom);
2103         
2104         this.triggerEl.on('click', this.onTriggerClick, this);
2105         
2106         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2107         
2108         this.triggerEl.addClass('dropdown-toggle');
2109         
2110         if (Roo.isTouch) {
2111             this.el.on('touchstart'  , this.onTouch, this);
2112         }
2113         this.el.on('click' , this.onClick, this);
2114
2115         this.el.on("mouseover", this.onMouseOver, this);
2116         this.el.on("mouseout", this.onMouseOut, this);
2117         
2118     },
2119     
2120     findTargetItem : function(e)
2121     {
2122         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2123         if(!t){
2124             return false;
2125         }
2126         //Roo.log(t);         Roo.log(t.id);
2127         if(t && t.id){
2128             //Roo.log(this.menuitems);
2129             return this.menuitems.get(t.id);
2130             
2131             //return this.items.get(t.menuItemId);
2132         }
2133         
2134         return false;
2135     },
2136     
2137     onTouch : function(e) 
2138     {
2139         Roo.log("menu.onTouch");
2140         //e.stopEvent(); this make the user popdown broken
2141         this.onClick(e);
2142     },
2143     
2144     onClick : function(e)
2145     {
2146         Roo.log("menu.onClick");
2147         
2148         var t = this.findTargetItem(e);
2149         if(!t || t.isContainer){
2150             return;
2151         }
2152         Roo.log(e);
2153         /*
2154         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2155             if(t == this.activeItem && t.shouldDeactivate(e)){
2156                 this.activeItem.deactivate();
2157                 delete this.activeItem;
2158                 return;
2159             }
2160             if(t.canActivate){
2161                 this.setActiveItem(t, true);
2162             }
2163             return;
2164             
2165             
2166         }
2167         */
2168        
2169         Roo.log('pass click event');
2170         
2171         t.onClick(e);
2172         
2173         this.fireEvent("click", this, t, e);
2174         
2175         var _this = this;
2176         
2177         if(!t.href.length || t.href == '#'){
2178             (function() { _this.hide(); }).defer(100);
2179         }
2180         
2181     },
2182     
2183     onMouseOver : function(e){
2184         var t  = this.findTargetItem(e);
2185         //Roo.log(t);
2186         //if(t){
2187         //    if(t.canActivate && !t.disabled){
2188         //        this.setActiveItem(t, true);
2189         //    }
2190         //}
2191         
2192         this.fireEvent("mouseover", this, e, t);
2193     },
2194     isVisible : function(){
2195         return !this.hidden;
2196     },
2197      onMouseOut : function(e){
2198         var t  = this.findTargetItem(e);
2199         
2200         //if(t ){
2201         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2202         //        this.activeItem.deactivate();
2203         //        delete this.activeItem;
2204         //    }
2205         //}
2206         this.fireEvent("mouseout", this, e, t);
2207     },
2208     
2209     
2210     /**
2211      * Displays this menu relative to another element
2212      * @param {String/HTMLElement/Roo.Element} element The element to align to
2213      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214      * the element (defaults to this.defaultAlign)
2215      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2216      */
2217     show : function(el, pos, parentMenu){
2218         this.parentMenu = parentMenu;
2219         if(!this.el){
2220             this.render();
2221         }
2222         this.fireEvent("beforeshow", this);
2223         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2224     },
2225      /**
2226      * Displays this menu at a specific xy position
2227      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     showAt : function(xy, parentMenu, /* private: */_e){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         if(_e !== false){
2236             this.fireEvent("beforeshow", this);
2237             //xy = this.el.adjustForConstraints(xy);
2238         }
2239         
2240         //this.el.show();
2241         this.hideMenuItems();
2242         this.hidden = false;
2243         this.triggerEl.addClass('open');
2244         
2245         // reassign x when hitting right
2246         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2248         }
2249         
2250         // reassign y when hitting bottom
2251         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2253         }
2254         
2255         // but the list may align on trigger left or trigger top... should it be a properity?
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  * @cfg {Number} max_width set the max width of modal
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628     
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637     
2638     max_width: 0,
2639     
2640     max_height: 0,
2641     
2642     fit_content: false,
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         
2681         //this.el.addClass("x-dlg-modal");
2682
2683         if (this.buttons.length) {
2684             Roo.each(this.buttons, function(bb) {
2685                 var b = Roo.apply({}, bb);
2686                 b.xns = b.xns || Roo.bootstrap;
2687                 b.xtype = b.xtype || 'Button';
2688                 if (typeof(b.listeners) == 'undefined') {
2689                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2690                 }
2691
2692                 var btn = Roo.factory(b);
2693
2694                 btn.render(this.el.select('.modal-footer div').first());
2695
2696             },this);
2697         }
2698         // render the children.
2699         var nitems = [];
2700
2701         if(typeof(this.items) != 'undefined'){
2702             var items = this.items;
2703             delete this.items;
2704
2705             for(var i =0;i < items.length;i++) {
2706                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2707             }
2708         }
2709
2710         this.items = nitems;
2711
2712         // where are these used - they used to be body/close/footer
2713
2714
2715         this.initEvents();
2716         //this.el.addClass([this.fieldClass, this.cls]);
2717
2718     },
2719
2720     getAutoCreate : function()
2721     {
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(
2819             Roo.lib.Dom.getViewWidth(true),
2820             Roo.lib.Dom.getViewHeight(true)
2821         );
2822         
2823         if (this.fitwindow) {
2824             this.setSize(
2825                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2827             );
2828             return;
2829         }
2830         
2831         if(this.max_width !== 0) {
2832             
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             if(this.height) {
2836                 this.setSize(w, this.height);
2837                 return;
2838             }
2839             
2840             if(this.max_height) {
2841                 this.setSize(w,Math.min(
2842                     this.max_height,
2843                     Roo.lib.Dom.getViewportHeight(true) - 60
2844                 ));
2845                 
2846                 return;
2847             }
2848             
2849             if(!this.fit_content) {
2850                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2851                 return;
2852             }
2853             
2854             this.setSize(w, Math.min(
2855                 60 +
2856                 this.headerEl.getHeight() + 
2857                 this.footerEl.getHeight() + 
2858                 this.getChildHeight(this.bodyEl.dom.childNodes),
2859                 Roo.lib.Dom.getViewportHeight(true) - 60)
2860             );
2861         }
2862         
2863     },
2864
2865     setSize : function(w,h)
2866     {
2867         if (!w && !h) {
2868             return;
2869         }
2870         
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h - this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     },
3024     
3025     getChildHeight : function(child_nodes)
3026     {
3027         if(
3028             !child_nodes ||
3029             child_nodes.length == 0
3030         ) {
3031             return;
3032         }
3033         
3034         var child_height = 0;
3035         
3036         for(var i = 0; i < child_nodes.length; i++) {
3037             
3038             /*
3039             * for modal with tabs...
3040             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3041                 
3042                 var layout_childs = child_nodes[i].childNodes;
3043                 
3044                 for(var j = 0; j < layout_childs.length; j++) {
3045                     
3046                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3047                         
3048                         var layout_body_childs = layout_childs[j].childNodes;
3049                         
3050                         for(var k = 0; k < layout_body_childs.length; k++) {
3051                             
3052                             if(layout_body_childs[k].classList.contains('navbar')) {
3053                                 child_height += layout_body_childs[k].offsetHeight;
3054                                 continue;
3055                             }
3056                             
3057                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3058                                 
3059                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3060                                 
3061                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3062                                     
3063                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3065                                         continue;
3066                                     }
3067                                     
3068                                 }
3069                                 
3070                             }
3071                             
3072                         }
3073                     }
3074                 }
3075                 continue;
3076             }
3077             */
3078             
3079             child_height += child_nodes[i].offsetHeight;
3080             // Roo.log(child_nodes[i].offsetHeight);
3081         }
3082         
3083         return child_height;
3084     }
3085
3086 });
3087
3088
3089 Roo.apply(Roo.bootstrap.Modal,  {
3090     /**
3091          * Button config that displays a single OK button
3092          * @type Object
3093          */
3094         OK :  [{
3095             name : 'ok',
3096             weight : 'primary',
3097             html : 'OK'
3098         }],
3099         /**
3100          * Button config that displays Yes and No buttons
3101          * @type Object
3102          */
3103         YESNO : [
3104             {
3105                 name  : 'no',
3106                 html : 'No'
3107             },
3108             {
3109                 name  :'yes',
3110                 weight : 'primary',
3111                 html : 'Yes'
3112             }
3113         ],
3114
3115         /**
3116          * Button config that displays OK and Cancel buttons
3117          * @type Object
3118          */
3119         OKCANCEL : [
3120             {
3121                name : 'cancel',
3122                 html : 'Cancel'
3123             },
3124             {
3125                 name : 'ok',
3126                 weight : 'primary',
3127                 html : 'OK'
3128             }
3129         ],
3130         /**
3131          * Button config that displays Yes, No and Cancel buttons
3132          * @type Object
3133          */
3134         YESNOCANCEL : [
3135             {
3136                 name : 'yes',
3137                 weight : 'primary',
3138                 html : 'Yes'
3139             },
3140             {
3141                 name : 'no',
3142                 html : 'No'
3143             },
3144             {
3145                 name : 'cancel',
3146                 html : 'Cancel'
3147             }
3148         ],
3149         
3150         zIndex : 10001
3151 });
3152 /*
3153  * - LGPL
3154  *
3155  * messagebox - can be used as a replace
3156  * 
3157  */
3158 /**
3159  * @class Roo.MessageBox
3160  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3161  * Example usage:
3162  *<pre><code>
3163 // Basic alert:
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3165
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3168     if (btn == 'ok'){
3169         // process text value...
3170     }
3171 });
3172
3173 // Show a dialog using config options:
3174 Roo.Msg.show({
3175    title:'Save Changes?',
3176    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177    buttons: Roo.Msg.YESNOCANCEL,
3178    fn: processResult,
3179    animEl: 'elId'
3180 });
3181 </code></pre>
3182  * @singleton
3183  */
3184 Roo.bootstrap.MessageBox = function(){
3185     var dlg, opt, mask, waitTimer;
3186     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187     var buttons, activeTextEl, bwidth;
3188
3189     
3190     // private
3191     var handleButton = function(button){
3192         dlg.hide();
3193         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3194     };
3195
3196     // private
3197     var handleHide = function(){
3198         if(opt && opt.cls){
3199             dlg.el.removeClass(opt.cls);
3200         }
3201         //if(waitTimer){
3202         //    Roo.TaskMgr.stop(waitTimer);
3203         //    waitTimer = null;
3204         //}
3205     };
3206
3207     // private
3208     var updateButtons = function(b){
3209         var width = 0;
3210         if(!b){
3211             buttons["ok"].hide();
3212             buttons["cancel"].hide();
3213             buttons["yes"].hide();
3214             buttons["no"].hide();
3215             //dlg.footer.dom.style.display = 'none';
3216             return width;
3217         }
3218         dlg.footerEl.dom.style.display = '';
3219         for(var k in buttons){
3220             if(typeof buttons[k] != "function"){
3221                 if(b[k]){
3222                     buttons[k].show();
3223                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224                     width += buttons[k].el.getWidth()+15;
3225                 }else{
3226                     buttons[k].hide();
3227                 }
3228             }
3229         }
3230         return width;
3231     };
3232
3233     // private
3234     var handleEsc = function(d, k, e){
3235         if(opt && opt.closable !== false){
3236             dlg.hide();
3237         }
3238         if(e){
3239             e.stopEvent();
3240         }
3241     };
3242
3243     return {
3244         /**
3245          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246          * @return {Roo.BasicDialog} The BasicDialog element
3247          */
3248         getDialog : function(){
3249            if(!dlg){
3250                 dlg = new Roo.bootstrap.Modal( {
3251                     //draggable: true,
3252                     //resizable:false,
3253                     //constraintoviewport:false,
3254                     //fixedcenter:true,
3255                     //collapsible : false,
3256                     //shim:true,
3257                     //modal: true,
3258                 //    width: 'auto',
3259                   //  height:100,
3260                     //buttonAlign:"center",
3261                     closeClick : function(){
3262                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3263                             handleButton("no");
3264                         }else{
3265                             handleButton("cancel");
3266                         }
3267                     }
3268                 });
3269                 dlg.render();
3270                 dlg.on("hide", handleHide);
3271                 mask = dlg.mask;
3272                 //dlg.addKeyListener(27, handleEsc);
3273                 buttons = {};
3274                 this.buttons = buttons;
3275                 var bt = this.buttonText;
3276                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3280                 //Roo.log(buttons);
3281                 bodyEl = dlg.bodyEl.createChild({
3282
3283                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284                         '<textarea class="roo-mb-textarea"></textarea>' +
3285                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3286                 });
3287                 msgEl = bodyEl.dom.firstChild;
3288                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289                 textboxEl.enableDisplayMode();
3290                 textboxEl.addKeyListener([10,13], function(){
3291                     if(dlg.isVisible() && opt && opt.buttons){
3292                         if(opt.buttons.ok){
3293                             handleButton("ok");
3294                         }else if(opt.buttons.yes){
3295                             handleButton("yes");
3296                         }
3297                     }
3298                 });
3299                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300                 textareaEl.enableDisplayMode();
3301                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302                 progressEl.enableDisplayMode();
3303                 
3304                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305                 var pf = progressEl.dom.firstChild;
3306                 if (pf) {
3307                     pp = Roo.get(pf.firstChild);
3308                     pp.setHeight(pf.offsetHeight);
3309                 }
3310                 
3311             }
3312             return dlg;
3313         },
3314
3315         /**
3316          * Updates the message box body text
3317          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318          * the XHTML-compliant non-breaking space character '&amp;#160;')
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         updateText : function(text)
3322         {
3323             if(!dlg.isVisible() && !opt.width){
3324                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3326             }
3327             msgEl.innerHTML = text || '&#160;';
3328       
3329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3331             var w = Math.max(
3332                     Math.min(opt.width || cw , this.maxWidth), 
3333                     Math.max(opt.minWidth || this.minWidth, bwidth)
3334             );
3335             if(opt.prompt){
3336                 activeTextEl.setWidth(w);
3337             }
3338             if(dlg.isVisible()){
3339                 dlg.fixedcenter = false;
3340             }
3341             // to big, make it scroll. = But as usual stupid IE does not support
3342             // !important..
3343             
3344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3347             } else {
3348                 bodyEl.dom.style.height = '';
3349                 bodyEl.dom.style.overflowY = '';
3350             }
3351             if (cw > w) {
3352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3353             } else {
3354                 bodyEl.dom.style.overflowX = '';
3355             }
3356             
3357             dlg.setContentSize(w, bodyEl.getHeight());
3358             if(dlg.isVisible()){
3359                 dlg.fixedcenter = true;
3360             }
3361             return this;
3362         },
3363
3364         /**
3365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         updateProgress : function(value, text){
3372             if(text){
3373                 this.updateText(text);
3374             }
3375             
3376             if (pp) { // weird bug on my firefox - for some reason this is not defined
3377                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3379             }
3380             return this;
3381         },        
3382
3383         /**
3384          * Returns true if the message box is currently displayed
3385          * @return {Boolean} True if the message box is visible, else false
3386          */
3387         isVisible : function(){
3388             return dlg && dlg.isVisible();  
3389         },
3390
3391         /**
3392          * Hides the message box if it is displayed
3393          */
3394         hide : function(){
3395             if(this.isVisible()){
3396                 dlg.hide();
3397             }  
3398         },
3399
3400         /**
3401          * Displays a new message box, or reinitializes an existing message box, based on the config options
3402          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403          * The following config object properties are supported:
3404          * <pre>
3405 Property    Type             Description
3406 ----------  ---------------  ------------------------------------------------------------------------------------
3407 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3408                                    closes (defaults to undefined)
3409 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3412                                    progress and wait dialogs will ignore this property and always hide the
3413                                    close button as they can only be closed programmatically.
3414 cls               String           A custom CSS class to apply to the message box element
3415 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3416                                    displayed (defaults to 75)
3417 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3418                                    function will be btn (the name of the button that was clicked, if applicable,
3419                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3420                                    Progress and wait dialogs will ignore this option since they do not respond to
3421                                    user actions and can only be closed programmatically, so any required function
3422                                    should be called by the same code after it closes the dialog.
3423 icon              String           A CSS class that provides a background image to be used as an icon for
3424                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3426 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3427 modal             Boolean          False to allow user interaction with the page while the message box is
3428                                    displayed (defaults to true)
3429 msg               String           A string that will replace the existing message box body text (defaults
3430                                    to the XHTML-compliant non-breaking space character '&#160;')
3431 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3432 progress          Boolean          True to display a progress bar (defaults to false)
3433 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3436 title             String           The title text
3437 value             String           The string value to set into the active textbox element if displayed
3438 wait              Boolean          True to display a progress bar (defaults to false)
3439 width             Number           The width of the dialog in pixels
3440 </pre>
3441          *
3442          * Example usage:
3443          * <pre><code>
3444 Roo.Msg.show({
3445    title: 'Address',
3446    msg: 'Please enter your address:',
3447    width: 300,
3448    buttons: Roo.MessageBox.OKCANCEL,
3449    multiline: true,
3450    fn: saveAddress,
3451    animEl: 'addAddressBtn'
3452 });
3453 </code></pre>
3454          * @param {Object} config Configuration options
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         show : function(options)
3458         {
3459             
3460             // this causes nightmares if you show one dialog after another
3461             // especially on callbacks..
3462              
3463             if(this.isVisible()){
3464                 
3465                 this.hide();
3466                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3468                 Roo.log("New Dialog Message:" +  options.msg )
3469                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3471                 
3472             }
3473             var d = this.getDialog();
3474             opt = options;
3475             d.setTitle(opt.title || "&#160;");
3476             d.closeEl.setDisplayed(opt.closable !== false);
3477             activeTextEl = textboxEl;
3478             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3479             if(opt.prompt){
3480                 if(opt.multiline){
3481                     textboxEl.hide();
3482                     textareaEl.show();
3483                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3484                         opt.multiline : this.defaultTextHeight);
3485                     activeTextEl = textareaEl;
3486                 }else{
3487                     textboxEl.show();
3488                     textareaEl.hide();
3489                 }
3490             }else{
3491                 textboxEl.hide();
3492                 textareaEl.hide();
3493             }
3494             progressEl.setDisplayed(opt.progress === true);
3495             this.updateProgress(0);
3496             activeTextEl.dom.value = opt.value || "";
3497             if(opt.prompt){
3498                 dlg.setDefaultButton(activeTextEl);
3499             }else{
3500                 var bs = opt.buttons;
3501                 var db = null;
3502                 if(bs && bs.ok){
3503                     db = buttons["ok"];
3504                 }else if(bs && bs.yes){
3505                     db = buttons["yes"];
3506                 }
3507                 dlg.setDefaultButton(db);
3508             }
3509             bwidth = updateButtons(opt.buttons);
3510             this.updateText(opt.msg);
3511             if(opt.cls){
3512                 d.el.addClass(opt.cls);
3513             }
3514             d.proxyDrag = opt.proxyDrag === true;
3515             d.modal = opt.modal !== false;
3516             d.mask = opt.modal !== false ? mask : false;
3517             if(!d.isVisible()){
3518                 // force it to the end of the z-index stack so it gets a cursor in FF
3519                 document.body.appendChild(dlg.el.dom);
3520                 d.animateTarget = null;
3521                 d.show(options.animEl);
3522             }
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3528          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529          * and closing the message box when the process is complete.
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         progress : function(title, msg){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: false,
3539                 progress:true,
3540                 closable:false,
3541                 minWidth: this.minProgressWidth,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549          * If a callback function is passed it will be called after the user clicks the button, and the
3550          * id of the button that was clicked will be passed as the only parameter to the callback
3551          * (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         alert : function(title, msg, fn, scope)
3559         {
3560             this.show({
3561                 title : title,
3562                 msg : msg,
3563                 buttons: this.OK,
3564                 fn: fn,
3565                 closable : false,
3566                 scope : scope,
3567                 modal : true
3568             });
3569             return this;
3570         },
3571
3572         /**
3573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575          * You are responsible for closing the message box when the process is complete.
3576          * @param {String} msg The message box body text
3577          * @param {String} title (optional) The title bar text
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         wait : function(msg, title){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: false,
3585                 closable:false,
3586                 progress:true,
3587                 modal:true,
3588                 width:300,
3589                 wait:true
3590             });
3591             waitTimer = Roo.TaskMgr.start({
3592                 run: function(i){
3593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594                 },
3595                 interval: 1000
3596             });
3597             return this;
3598         },
3599
3600         /**
3601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604          * @param {String} title The title bar text
3605          * @param {String} msg The message box body text
3606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607          * @param {Object} scope (optional) The scope of the callback function
3608          * @return {Roo.MessageBox} This message box
3609          */
3610         confirm : function(title, msg, fn, scope){
3611             this.show({
3612                 title : title,
3613                 msg : msg,
3614                 buttons: this.YESNO,
3615                 fn: fn,
3616                 scope : scope,
3617                 modal : true
3618             });
3619             return this;
3620         },
3621
3622         /**
3623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626          * (could also be the top-right close button) and the text that was entered will be passed as the two
3627          * parameters to the callback.
3628          * @param {String} title The title bar text
3629          * @param {String} msg The message box body text
3630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631          * @param {Object} scope (optional) The scope of the callback function
3632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634          * @return {Roo.MessageBox} This message box
3635          */
3636         prompt : function(title, msg, fn, scope, multiline){
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OKCANCEL,
3641                 fn: fn,
3642                 minWidth:250,
3643                 scope : scope,
3644                 prompt:true,
3645                 multiline: multiline,
3646                 modal : true
3647             });
3648             return this;
3649         },
3650
3651         /**
3652          * Button config that displays a single OK button
3653          * @type Object
3654          */
3655         OK : {ok:true},
3656         /**
3657          * Button config that displays Yes and No buttons
3658          * @type Object
3659          */
3660         YESNO : {yes:true, no:true},
3661         /**
3662          * Button config that displays OK and Cancel buttons
3663          * @type Object
3664          */
3665         OKCANCEL : {ok:true, cancel:true},
3666         /**
3667          * Button config that displays Yes, No and Cancel buttons
3668          * @type Object
3669          */
3670         YESNOCANCEL : {yes:true, no:true, cancel:true},
3671
3672         /**
3673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3674          * @type Number
3675          */
3676         defaultTextHeight : 75,
3677         /**
3678          * The maximum width in pixels of the message box (defaults to 600)
3679          * @type Number
3680          */
3681         maxWidth : 600,
3682         /**
3683          * The minimum width in pixels of the message box (defaults to 100)
3684          * @type Number
3685          */
3686         minWidth : 100,
3687         /**
3688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3690          * @type Number
3691          */
3692         minProgressWidth : 250,
3693         /**
3694          * An object containing the default button text strings that can be overriden for localized language support.
3695          * Supported properties are: ok, cancel, yes and no.
3696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3697          * @type Object
3698          */
3699         buttonText : {
3700             ok : "OK",
3701             cancel : "Cancel",
3702             yes : "Yes",
3703             no : "No"
3704         }
3705     };
3706 }();
3707
3708 /**
3709  * Shorthand for {@link Roo.MessageBox}
3710  */
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3713 /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.Navbar
3722  * @extends Roo.bootstrap.Component
3723  * Bootstrap Navbar class
3724
3725  * @constructor
3726  * Create a new Navbar
3727  * @param {Object} config The config object
3728  */
3729
3730
3731 Roo.bootstrap.Navbar = function(config){
3732     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3733     this.addEvents({
3734         // raw events
3735         /**
3736          * @event beforetoggle
3737          * Fire before toggle the menu
3738          * @param {Roo.EventObject} e
3739          */
3740         "beforetoggle" : true
3741     });
3742 };
3743
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3745     
3746     
3747    
3748     // private
3749     navItems : false,
3750     loadMask : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3757         
3758     },
3759     
3760     initEvents :function ()
3761     {
3762         //Roo.log(this.el.select('.navbar-toggle',true));
3763         this.el.select('.navbar-toggle',true).on('click', function() {
3764             if(this.fireEvent('beforetoggle', this) !== false){
3765                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3766             }
3767             
3768         }, this);
3769         
3770         var mark = {
3771             tag: "div",
3772             cls:"x-dlg-mask"
3773         };
3774         
3775         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3776         
3777         var size = this.el.getSize();
3778         this.maskEl.setSize(size.width, size.height);
3779         this.maskEl.enableDisplayMode("block");
3780         this.maskEl.hide();
3781         
3782         if(this.loadMask){
3783             this.maskEl.show();
3784         }
3785     },
3786     
3787     
3788     getChildContainer : function()
3789     {
3790         if (this.el.select('.collapse').getCount()) {
3791             return this.el.select('.collapse',true).first();
3792         }
3793         
3794         return this.el;
3795     },
3796     
3797     mask : function()
3798     {
3799         this.maskEl.show();
3800     },
3801     
3802     unmask : function()
3803     {
3804         this.maskEl.hide();
3805     } 
3806     
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  /*
3817  * - LGPL
3818  *
3819  * navbar
3820  * 
3821  */
3822
3823 /**
3824  * @class Roo.bootstrap.NavSimplebar
3825  * @extends Roo.bootstrap.Navbar
3826  * Bootstrap Sidebar class
3827  *
3828  * @cfg {Boolean} inverse is inverted color
3829  * 
3830  * @cfg {String} type (nav | pills | tabs)
3831  * @cfg {Boolean} arrangement stacked | justified
3832  * @cfg {String} align (left | right) alignment
3833  * 
3834  * @cfg {Boolean} main (true|false) main nav bar? default false
3835  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3836  * 
3837  * @cfg {String} tag (header|footer|nav|div) default is nav 
3838
3839  * 
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new Sidebar
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.NavSimplebar = function(config){
3849     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3853     
3854     inverse: false,
3855     
3856     type: false,
3857     arrangement: '',
3858     align : false,
3859     
3860     
3861     
3862     main : false,
3863     
3864     
3865     tag : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         var cfg = {
3872             tag : this.tag || 'div',
3873             cls : 'navbar'
3874         };
3875           
3876         
3877         cfg.cn = [
3878             {
3879                 cls: 'nav',
3880                 tag : 'ul'
3881             }
3882         ];
3883         
3884          
3885         this.type = this.type || 'nav';
3886         if (['tabs','pills'].indexOf(this.type)!==-1) {
3887             cfg.cn[0].cls += ' nav-' + this.type
3888         
3889         
3890         } else {
3891             if (this.type!=='nav') {
3892                 Roo.log('nav type must be nav/tabs/pills')
3893             }
3894             cfg.cn[0].cls += ' navbar-nav'
3895         }
3896         
3897         
3898         
3899         
3900         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.arrangement;
3902         }
3903         
3904         
3905         if (this.align === 'right') {
3906             cfg.cn[0].cls += ' navbar-right';
3907         }
3908         
3909         if (this.inverse) {
3910             cfg.cls += ' navbar-inverse';
3911             
3912         }
3913         
3914         
3915         return cfg;
3916     
3917         
3918     }
3919     
3920     
3921     
3922 });
3923
3924
3925
3926  
3927
3928  
3929        /*
3930  * - LGPL
3931  *
3932  * navbar
3933  * navbar-fixed-top
3934  * navbar-expand-md  fixed-top 
3935  */
3936
3937 /**
3938  * @class Roo.bootstrap.NavHeaderbar
3939  * @extends Roo.bootstrap.NavSimplebar
3940  * Bootstrap Sidebar class
3941  *
3942  * @cfg {String} brand what is brand
3943  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944  * @cfg {String} brand_href href of the brand
3945  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3946  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3949  * 
3950  * @constructor
3951  * Create a new Sidebar
3952  * @param {Object} config The config object
3953  */
3954
3955
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3958       
3959 };
3960
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3962     
3963     position: '',
3964     brand: '',
3965     brand_href: false,
3966     srButton : true,
3967     autohide : false,
3968     desktopCenter : false,
3969    
3970     
3971     getAutoCreate : function(){
3972         
3973         var   cfg = {
3974             tag: this.nav || 'nav',
3975             cls: 'navbar navbar-expand-md',
3976             role: 'navigation',
3977             cn: []
3978         };
3979         
3980         var cn = cfg.cn;
3981         if (this.desktopCenter) {
3982             cn.push({cls : 'container', cn : []});
3983             cn = cn[0].cn;
3984         }
3985         
3986         if(this.srButton){
3987             cn.push({
3988                 tag: 'div',
3989                 cls: 'navbar-header',
3990                 cn: [
3991                     {
3992                         tag: 'button',
3993                         type: 'button',
3994                         cls: 'navbar-toggle navbar-toggler',
3995                         'data-toggle': 'collapse',
3996                         cn: [
3997                             {
3998                                 tag: 'span',
3999                                 cls: 'sr-only',
4000                                 html: 'Toggle navigation'
4001                             },
4002                             {
4003                                 tag: 'span',
4004                                 cls: 'icon-bar navbar-toggler-icon'
4005                             },
4006                             {
4007                                 tag: 'span',
4008                                 cls: 'icon-bar'
4009                             },
4010                             {
4011                                 tag: 'span',
4012                                 cls: 'icon-bar'
4013                             }
4014                         ]
4015                     }
4016                 ]
4017             });
4018         }
4019         
4020         cn.push({
4021             tag: 'div',
4022             cls: 'collapse navbar-collapse',
4023             cn : []
4024         });
4025         
4026         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4027         
4028         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4030             
4031             // tag can override this..
4032             
4033             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4034         }
4035         
4036         if (this.brand !== '') {
4037             cn[0].cn.push({
4038                 tag: 'a',
4039                 href: this.brand_href ? this.brand_href : '#',
4040                 cls: 'navbar-brand',
4041                 cn: [
4042                 this.brand
4043                 ]
4044             });
4045         }
4046         
4047         if(this.main){
4048             cfg.cls += ' main-nav';
4049         }
4050         
4051         
4052         return cfg;
4053
4054         
4055     },
4056     getHeaderChildContainer : function()
4057     {
4058         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059             return this.el.select('.navbar-header',true).first();
4060         }
4061         
4062         return this.getChildContainer();
4063     },
4064     
4065     
4066     initEvents : function()
4067     {
4068         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069         
4070         if (this.autohide) {
4071             
4072             var prevScroll = 0;
4073             var ft = this.el;
4074             
4075             Roo.get(document).on('scroll',function(e) {
4076                 var ns = Roo.get(document).getScroll().top;
4077                 var os = prevScroll;
4078                 prevScroll = ns;
4079                 
4080                 if(ns > os){
4081                     ft.removeClass('slideDown');
4082                     ft.addClass('slideUp');
4083                     return;
4084                 }
4085                 ft.removeClass('slideUp');
4086                 ft.addClass('slideDown');
4087                  
4088               
4089           },this);
4090         }
4091     }    
4092     
4093 });
4094
4095
4096
4097  
4098
4099  /*
4100  * - LGPL
4101  *
4102  * navbar
4103  * 
4104  */
4105
4106 /**
4107  * @class Roo.bootstrap.NavSidebar
4108  * @extends Roo.bootstrap.Navbar
4109  * Bootstrap Sidebar class
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavSidebar = function(config){
4118     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4119 };
4120
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4122     
4123     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124     
4125     getAutoCreate : function(){
4126         
4127         
4128         return  {
4129             tag: 'div',
4130             cls: 'sidebar sidebar-nav'
4131         };
4132     
4133         
4134     }
4135     
4136     
4137     
4138 });
4139
4140
4141
4142  
4143
4144  /*
4145  * - LGPL
4146  *
4147  * nav group
4148  * 
4149  */
4150
4151 /**
4152  * @class Roo.bootstrap.NavGroup
4153  * @extends Roo.bootstrap.Component
4154  * Bootstrap NavGroup class
4155  * @cfg {String} align (left|right)
4156  * @cfg {Boolean} inverse
4157  * @cfg {String} type (nav|pills|tab) default nav
4158  * @cfg {String} navId - reference Id for navbar.
4159
4160  * 
4161  * @constructor
4162  * Create a new nav group
4163  * @param {Object} config The config object
4164  */
4165
4166 Roo.bootstrap.NavGroup = function(config){
4167     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4168     this.navItems = [];
4169    
4170     Roo.bootstrap.NavGroup.register(this);
4171      this.addEvents({
4172         /**
4173              * @event changed
4174              * Fires when the active item changes
4175              * @param {Roo.bootstrap.NavGroup} this
4176              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4178          */
4179         'changed': true
4180      });
4181     
4182 };
4183
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4185     
4186     align: '',
4187     inverse: false,
4188     form: false,
4189     type: 'nav',
4190     navId : '',
4191     // private
4192     
4193     navItems : false, 
4194     
4195     getAutoCreate : function()
4196     {
4197         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4198         
4199         cfg = {
4200             tag : 'ul',
4201             cls: 'nav' 
4202         };
4203         
4204         if (['tabs','pills'].indexOf(this.type)!==-1) {
4205             cfg.cls += ' nav-' + this.type
4206         } else {
4207             if (this.type!=='nav') {
4208                 Roo.log('nav type must be nav/tabs/pills')
4209             }
4210             cfg.cls += ' navbar-nav mr-auto'
4211         }
4212         
4213         if (this.parent() && this.parent().sidebar) {
4214             cfg = {
4215                 tag: 'ul',
4216                 cls: 'dashboard-menu sidebar-menu'
4217             };
4218             
4219             return cfg;
4220         }
4221         
4222         if (this.form === true) {
4223             cfg = {
4224                 tag: 'form',
4225                 cls: 'navbar-form'
4226             };
4227             
4228             if (this.align === 'right') {
4229                 cfg.cls += ' navbar-right';
4230             } else {
4231                 cfg.cls += ' navbar-left';
4232             }
4233         }
4234         
4235         if (this.align === 'right') {
4236             cfg.cls += ' navbar-right';
4237         }
4238         
4239         if (this.inverse) {
4240             cfg.cls += ' navbar-inverse';
4241             
4242         }
4243         
4244         
4245         return cfg;
4246     },
4247     /**
4248     * sets the active Navigation item
4249     * @param {Roo.bootstrap.NavItem} the new current navitem
4250     */
4251     setActiveItem : function(item)
4252     {
4253         var prev = false;
4254         Roo.each(this.navItems, function(v){
4255             if (v == item) {
4256                 return ;
4257             }
4258             if (v.isActive()) {
4259                 v.setActive(false, true);
4260                 prev = v;
4261                 
4262             }
4263             
4264         });
4265
4266         item.setActive(true, true);
4267         this.fireEvent('changed', this, item, prev);
4268         
4269         
4270     },
4271     /**
4272     * gets the active Navigation item
4273     * @return {Roo.bootstrap.NavItem} the current navitem
4274     */
4275     getActive : function()
4276     {
4277         
4278         var prev = false;
4279         Roo.each(this.navItems, function(v){
4280             
4281             if (v.isActive()) {
4282                 prev = v;
4283                 
4284             }
4285             
4286         });
4287         return prev;
4288     },
4289     
4290     indexOfNav : function()
4291     {
4292         
4293         var prev = false;
4294         Roo.each(this.navItems, function(v,i){
4295             
4296             if (v.isActive()) {
4297                 prev = i;
4298                 
4299             }
4300             
4301         });
4302         return prev;
4303     },
4304     /**
4305     * adds a Navigation item
4306     * @param {Roo.bootstrap.NavItem} the navitem to add
4307     */
4308     addItem : function(cfg)
4309     {
4310         var cn = new Roo.bootstrap.NavItem(cfg);
4311         this.register(cn);
4312         cn.parentId = this.id;
4313         cn.onRender(this.el, null);
4314         return cn;
4315     },
4316     /**
4317     * register a Navigation item
4318     * @param {Roo.bootstrap.NavItem} the navitem to add
4319     */
4320     register : function(item)
4321     {
4322         this.navItems.push( item);
4323         item.navId = this.navId;
4324     
4325     },
4326     
4327     /**
4328     * clear all the Navigation item
4329     */
4330    
4331     clearAll : function()
4332     {
4333         this.navItems = [];
4334         this.el.dom.innerHTML = '';
4335     },
4336     
4337     getNavItem: function(tabId)
4338     {
4339         var ret = false;
4340         Roo.each(this.navItems, function(e) {
4341             if (e.tabId == tabId) {
4342                ret =  e;
4343                return false;
4344             }
4345             return true;
4346             
4347         });
4348         return ret;
4349     },
4350     
4351     setActiveNext : function()
4352     {
4353         var i = this.indexOfNav(this.getActive());
4354         if (i > this.navItems.length) {
4355             return;
4356         }
4357         this.setActiveItem(this.navItems[i+1]);
4358     },
4359     setActivePrev : function()
4360     {
4361         var i = this.indexOfNav(this.getActive());
4362         if (i  < 1) {
4363             return;
4364         }
4365         this.setActiveItem(this.navItems[i-1]);
4366     },
4367     clearWasActive : function(except) {
4368         Roo.each(this.navItems, function(e) {
4369             if (e.tabId != except.tabId && e.was_active) {
4370                e.was_active = false;
4371                return false;
4372             }
4373             return true;
4374             
4375         });
4376     },
4377     getWasActive : function ()
4378     {
4379         var r = false;
4380         Roo.each(this.navItems, function(e) {
4381             if (e.was_active) {
4382                r = e;
4383                return false;
4384             }
4385             return true;
4386             
4387         });
4388         return r;
4389     }
4390     
4391     
4392 });
4393
4394  
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4396     
4397     groups: {},
4398      /**
4399     * register a Navigation Group
4400     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401     */
4402     register : function(navgrp)
4403     {
4404         this.groups[navgrp.navId] = navgrp;
4405         
4406     },
4407     /**
4408     * fetch a Navigation Group based on the navigation ID
4409     * @param {string} the navgroup to add
4410     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4411     */
4412     get: function(navId) {
4413         if (typeof(this.groups[navId]) == 'undefined') {
4414             return false;
4415             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416         }
4417         return this.groups[navId] ;
4418     }
4419     
4420     
4421     
4422 });
4423
4424  /*
4425  * - LGPL
4426  *
4427  * row
4428  * 
4429  */
4430
4431 /**
4432  * @class Roo.bootstrap.NavItem
4433  * @extends Roo.bootstrap.Component
4434  * Bootstrap Navbar.NavItem class
4435  * @cfg {String} href  link to
4436  * @cfg {String} html content of button
4437  * @cfg {String} badge text inside badge
4438  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439  * @cfg {String} glyphicon name of glyphicon
4440  * @cfg {String} icon name of font awesome icon
4441  * @cfg {Boolean} active Is item active
4442  * @cfg {Boolean} disabled Is item disabled
4443  
4444  * @cfg {Boolean} preventDefault (true | false) default false
4445  * @cfg {String} tabId the tab that this item activates.
4446  * @cfg {String} tagtype (a|span) render as a href or span?
4447  * @cfg {Boolean} animateRef (true|false) link to element default false  
4448   
4449  * @constructor
4450  * Create a new Navbar Item
4451  * @param {Object} config The config object
4452  */
4453 Roo.bootstrap.NavItem = function(config){
4454     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * The raw click event for the entire grid.
4460          * @param {Roo.EventObject} e
4461          */
4462         "click" : true,
4463          /**
4464             * @event changed
4465             * Fires when the active item active state changes
4466             * @param {Roo.bootstrap.NavItem} this
4467             * @param {boolean} state the new state
4468              
4469          */
4470         'changed': true,
4471         /**
4472             * @event scrollto
4473             * Fires when scroll to element
4474             * @param {Roo.bootstrap.NavItem} this
4475             * @param {Object} options
4476             * @param {Roo.EventObject} e
4477              
4478          */
4479         'scrollto': true
4480     });
4481    
4482 };
4483
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4485     
4486     href: false,
4487     html: '',
4488     badge: '',
4489     icon: false,
4490     glyphicon: false,
4491     active: false,
4492     preventDefault : false,
4493     tabId : false,
4494     tagtype : 'a',
4495     disabled : false,
4496     animateRef : false,
4497     was_active : false,
4498     
4499     getAutoCreate : function(){
4500          
4501         var cfg = {
4502             tag: 'li',
4503             cls: 'nav-item'
4504             
4505         };
4506         
4507         if (this.active) {
4508             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509         }
4510         if (this.disabled) {
4511             cfg.cls += ' disabled';
4512         }
4513         
4514         if (this.href || this.html || this.glyphicon || this.icon) {
4515             cfg.cn = [
4516                 {
4517                     tag: this.tagtype,
4518                     href : this.href || "#",
4519                     html: this.html || ''
4520                 }
4521             ];
4522             if (this.tagtype == 'a') {
4523                 cfg.cn[0].cls = 'nav-link';
4524             }
4525             if (this.icon) {
4526                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4527             }
4528
4529             if(this.glyphicon) {
4530                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4531             }
4532             
4533             if (this.menu) {
4534                 
4535                 cfg.cn[0].html += " <span class='caret'></span>";
4536              
4537             }
4538             
4539             if (this.badge !== '') {
4540                  
4541                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4542             }
4543         }
4544         
4545         
4546         
4547         return cfg;
4548     },
4549     initEvents: function() 
4550     {
4551         if (typeof (this.menu) != 'undefined') {
4552             this.menu.parentType = this.xtype;
4553             this.menu.triggerEl = this.el;
4554             this.menu = this.addxtype(Roo.apply({}, this.menu));
4555         }
4556         
4557         this.el.select('a',true).on('click', this.onClick, this);
4558         
4559         if(this.tagtype == 'span'){
4560             this.el.select('span',true).on('click', this.onClick, this);
4561         }
4562        
4563         // at this point parent should be available..
4564         this.parent().register(this);
4565     },
4566     
4567     onClick : function(e)
4568     {
4569         if (e.getTarget('.dropdown-menu-item')) {
4570             // did you click on a menu itemm.... - then don't trigger onclick..
4571             return;
4572         }
4573         
4574         if(
4575                 this.preventDefault || 
4576                 this.href == '#' 
4577         ){
4578             Roo.log("NavItem - prevent Default?");
4579             e.preventDefault();
4580         }
4581         
4582         if (this.disabled) {
4583             return;
4584         }
4585         
4586         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4587         if (tg && tg.transition) {
4588             Roo.log("waiting for the transitionend");
4589             return;
4590         }
4591         
4592         
4593         
4594         //Roo.log("fire event clicked");
4595         if(this.fireEvent('click', this, e) === false){
4596             return;
4597         };
4598         
4599         if(this.tagtype == 'span'){
4600             return;
4601         }
4602         
4603         //Roo.log(this.href);
4604         var ael = this.el.select('a',true).first();
4605         //Roo.log(ael);
4606         
4607         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4608             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4609             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4610                 return; // ignore... - it's a 'hash' to another page.
4611             }
4612             Roo.log("NavItem - prevent Default?");
4613             e.preventDefault();
4614             this.scrollToElement(e);
4615         }
4616         
4617         
4618         var p =  this.parent();
4619    
4620         if (['tabs','pills'].indexOf(p.type)!==-1) {
4621             if (typeof(p.setActiveItem) !== 'undefined') {
4622                 p.setActiveItem(this);
4623             }
4624         }
4625         
4626         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4627         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4628             // remove the collapsed menu expand...
4629             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4630         }
4631     },
4632     
4633     isActive: function () {
4634         return this.active
4635     },
4636     setActive : function(state, fire, is_was_active)
4637     {
4638         if (this.active && !state && this.navId) {
4639             this.was_active = true;
4640             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4641             if (nv) {
4642                 nv.clearWasActive(this);
4643             }
4644             
4645         }
4646         this.active = state;
4647         
4648         if (!state ) {
4649             this.el.removeClass('active');
4650         } else if (!this.el.hasClass('active')) {
4651             this.el.addClass('active');
4652         }
4653         if (fire) {
4654             this.fireEvent('changed', this, state);
4655         }
4656         
4657         // show a panel if it's registered and related..
4658         
4659         if (!this.navId || !this.tabId || !state || is_was_active) {
4660             return;
4661         }
4662         
4663         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4664         if (!tg) {
4665             return;
4666         }
4667         var pan = tg.getPanelByName(this.tabId);
4668         if (!pan) {
4669             return;
4670         }
4671         // if we can not flip to new panel - go back to old nav highlight..
4672         if (false == tg.showPanel(pan)) {
4673             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4674             if (nv) {
4675                 var onav = nv.getWasActive();
4676                 if (onav) {
4677                     onav.setActive(true, false, true);
4678                 }
4679             }
4680             
4681         }
4682         
4683         
4684         
4685     },
4686      // this should not be here...
4687     setDisabled : function(state)
4688     {
4689         this.disabled = state;
4690         if (!state ) {
4691             this.el.removeClass('disabled');
4692         } else if (!this.el.hasClass('disabled')) {
4693             this.el.addClass('disabled');
4694         }
4695         
4696     },
4697     
4698     /**
4699      * Fetch the element to display the tooltip on.
4700      * @return {Roo.Element} defaults to this.el
4701      */
4702     tooltipEl : function()
4703     {
4704         return this.el.select('' + this.tagtype + '', true).first();
4705     },
4706     
4707     scrollToElement : function(e)
4708     {
4709         var c = document.body;
4710         
4711         /*
4712          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4713          */
4714         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4715             c = document.documentElement;
4716         }
4717         
4718         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4719         
4720         if(!target){
4721             return;
4722         }
4723
4724         var o = target.calcOffsetsTo(c);
4725         
4726         var options = {
4727             target : target,
4728             value : o[1]
4729         };
4730         
4731         this.fireEvent('scrollto', this, options, e);
4732         
4733         Roo.get(c).scrollTo('top', options.value, true);
4734         
4735         return;
4736     }
4737 });
4738  
4739
4740  /*
4741  * - LGPL
4742  *
4743  * sidebar item
4744  *
4745  *  li
4746  *    <span> icon </span>
4747  *    <span> text </span>
4748  *    <span>badge </span>
4749  */
4750
4751 /**
4752  * @class Roo.bootstrap.NavSidebarItem
4753  * @extends Roo.bootstrap.NavItem
4754  * Bootstrap Navbar.NavSidebarItem class
4755  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4756  * {Boolean} open is the menu open
4757  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4758  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4759  * {String} buttonSize (sm|md|lg)the extra classes for the button
4760  * {Boolean} showArrow show arrow next to the text (default true)
4761  * @constructor
4762  * Create a new Navbar Button
4763  * @param {Object} config The config object
4764  */
4765 Roo.bootstrap.NavSidebarItem = function(config){
4766     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4767     this.addEvents({
4768         // raw events
4769         /**
4770          * @event click
4771          * The raw click event for the entire grid.
4772          * @param {Roo.EventObject} e
4773          */
4774         "click" : true,
4775          /**
4776             * @event changed
4777             * Fires when the active item active state changes
4778             * @param {Roo.bootstrap.NavSidebarItem} this
4779             * @param {boolean} state the new state
4780              
4781          */
4782         'changed': true
4783     });
4784    
4785 };
4786
4787 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4788     
4789     badgeWeight : 'default',
4790     
4791     open: false,
4792     
4793     buttonView : false,
4794     
4795     buttonWeight : 'default',
4796     
4797     buttonSize : 'md',
4798     
4799     showArrow : true,
4800     
4801     getAutoCreate : function(){
4802         
4803         
4804         var a = {
4805                 tag: 'a',
4806                 href : this.href || '#',
4807                 cls: '',
4808                 html : '',
4809                 cn : []
4810         };
4811         
4812         if(this.buttonView){
4813             a = {
4814                 tag: 'button',
4815                 href : this.href || '#',
4816                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4817                 html : this.html,
4818                 cn : []
4819             };
4820         }
4821         
4822         var cfg = {
4823             tag: 'li',
4824             cls: '',
4825             cn: [ a ]
4826         };
4827         
4828         if (this.active) {
4829             cfg.cls += ' active';
4830         }
4831         
4832         if (this.disabled) {
4833             cfg.cls += ' disabled';
4834         }
4835         if (this.open) {
4836             cfg.cls += ' open x-open';
4837         }
4838         // left icon..
4839         if (this.glyphicon || this.icon) {
4840             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4841             a.cn.push({ tag : 'i', cls : c }) ;
4842         }
4843         
4844         if(!this.buttonView){
4845             var span = {
4846                 tag: 'span',
4847                 html : this.html || ''
4848             };
4849
4850             a.cn.push(span);
4851             
4852         }
4853         
4854         if (this.badge !== '') {
4855             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4856         }
4857         
4858         if (this.menu) {
4859             
4860             if(this.showArrow){
4861                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4862             }
4863             
4864             a.cls += ' dropdown-toggle treeview' ;
4865         }
4866         
4867         return cfg;
4868     },
4869     
4870     initEvents : function()
4871     { 
4872         if (typeof (this.menu) != 'undefined') {
4873             this.menu.parentType = this.xtype;
4874             this.menu.triggerEl = this.el;
4875             this.menu = this.addxtype(Roo.apply({}, this.menu));
4876         }
4877         
4878         this.el.on('click', this.onClick, this);
4879         
4880         if(this.badge !== ''){
4881             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4882         }
4883         
4884     },
4885     
4886     onClick : function(e)
4887     {
4888         if(this.disabled){
4889             e.preventDefault();
4890             return;
4891         }
4892         
4893         if(this.preventDefault){
4894             e.preventDefault();
4895         }
4896         
4897         this.fireEvent('click', this);
4898     },
4899     
4900     disable : function()
4901     {
4902         this.setDisabled(true);
4903     },
4904     
4905     enable : function()
4906     {
4907         this.setDisabled(false);
4908     },
4909     
4910     setDisabled : function(state)
4911     {
4912         if(this.disabled == state){
4913             return;
4914         }
4915         
4916         this.disabled = state;
4917         
4918         if (state) {
4919             this.el.addClass('disabled');
4920             return;
4921         }
4922         
4923         this.el.removeClass('disabled');
4924         
4925         return;
4926     },
4927     
4928     setActive : function(state)
4929     {
4930         if(this.active == state){
4931             return;
4932         }
4933         
4934         this.active = state;
4935         
4936         if (state) {
4937             this.el.addClass('active');
4938             return;
4939         }
4940         
4941         this.el.removeClass('active');
4942         
4943         return;
4944     },
4945     
4946     isActive: function () 
4947     {
4948         return this.active;
4949     },
4950     
4951     setBadge : function(str)
4952     {
4953         if(!this.badgeEl){
4954             return;
4955         }
4956         
4957         this.badgeEl.dom.innerHTML = str;
4958     }
4959     
4960    
4961      
4962  
4963 });
4964  
4965
4966  /*
4967  * - LGPL
4968  *
4969  * row
4970  * 
4971  */
4972
4973 /**
4974  * @class Roo.bootstrap.Row
4975  * @extends Roo.bootstrap.Component
4976  * Bootstrap Row class (contains columns...)
4977  * 
4978  * @constructor
4979  * Create a new Row
4980  * @param {Object} config The config object
4981  */
4982
4983 Roo.bootstrap.Row = function(config){
4984     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4985 };
4986
4987 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4988     
4989     getAutoCreate : function(){
4990        return {
4991             cls: 'row clearfix'
4992        };
4993     }
4994     
4995     
4996 });
4997
4998  
4999
5000  /*
5001  * - LGPL
5002  *
5003  * element
5004  * 
5005  */
5006
5007 /**
5008  * @class Roo.bootstrap.Element
5009  * @extends Roo.bootstrap.Component
5010  * Bootstrap Element class
5011  * @cfg {String} html contents of the element
5012  * @cfg {String} tag tag of the element
5013  * @cfg {String} cls class of the element
5014  * @cfg {Boolean} preventDefault (true|false) default false
5015  * @cfg {Boolean} clickable (true|false) default false
5016  * 
5017  * @constructor
5018  * Create a new Element
5019  * @param {Object} config The config object
5020  */
5021
5022 Roo.bootstrap.Element = function(config){
5023     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5024     
5025     this.addEvents({
5026         // raw events
5027         /**
5028          * @event click
5029          * When a element is chick
5030          * @param {Roo.bootstrap.Element} this
5031          * @param {Roo.EventObject} e
5032          */
5033         "click" : true
5034     });
5035 };
5036
5037 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5038     
5039     tag: 'div',
5040     cls: '',
5041     html: '',
5042     preventDefault: false, 
5043     clickable: false,
5044     
5045     getAutoCreate : function(){
5046         
5047         var cfg = {
5048             tag: this.tag,
5049             // cls: this.cls, double assign in parent class Component.js :: onRender
5050             html: this.html
5051         };
5052         
5053         return cfg;
5054     },
5055     
5056     initEvents: function() 
5057     {
5058         Roo.bootstrap.Element.superclass.initEvents.call(this);
5059         
5060         if(this.clickable){
5061             this.el.on('click', this.onClick, this);
5062         }
5063         
5064     },
5065     
5066     onClick : function(e)
5067     {
5068         if(this.preventDefault){
5069             e.preventDefault();
5070         }
5071         
5072         this.fireEvent('click', this, e);
5073     },
5074     
5075     getValue : function()
5076     {
5077         return this.el.dom.innerHTML;
5078     },
5079     
5080     setValue : function(value)
5081     {
5082         this.el.dom.innerHTML = value;
5083     }
5084    
5085 });
5086
5087  
5088
5089  /*
5090  * - LGPL
5091  *
5092  * pagination
5093  * 
5094  */
5095
5096 /**
5097  * @class Roo.bootstrap.Pagination
5098  * @extends Roo.bootstrap.Component
5099  * Bootstrap Pagination class
5100  * @cfg {String} size xs | sm | md | lg
5101  * @cfg {Boolean} inverse false | true
5102  * 
5103  * @constructor
5104  * Create a new Pagination
5105  * @param {Object} config The config object
5106  */
5107
5108 Roo.bootstrap.Pagination = function(config){
5109     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5110 };
5111
5112 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5113     
5114     cls: false,
5115     size: false,
5116     inverse: false,
5117     
5118     getAutoCreate : function(){
5119         var cfg = {
5120             tag: 'ul',
5121                 cls: 'pagination'
5122         };
5123         if (this.inverse) {
5124             cfg.cls += ' inverse';
5125         }
5126         if (this.html) {
5127             cfg.html=this.html;
5128         }
5129         if (this.cls) {
5130             cfg.cls += " " + this.cls;
5131         }
5132         return cfg;
5133     }
5134    
5135 });
5136
5137  
5138
5139  /*
5140  * - LGPL
5141  *
5142  * Pagination item
5143  * 
5144  */
5145
5146
5147 /**
5148  * @class Roo.bootstrap.PaginationItem
5149  * @extends Roo.bootstrap.Component
5150  * Bootstrap PaginationItem class
5151  * @cfg {String} html text
5152  * @cfg {String} href the link
5153  * @cfg {Boolean} preventDefault (true | false) default true
5154  * @cfg {Boolean} active (true | false) default false
5155  * @cfg {Boolean} disabled default false
5156  * 
5157  * 
5158  * @constructor
5159  * Create a new PaginationItem
5160  * @param {Object} config The config object
5161  */
5162
5163
5164 Roo.bootstrap.PaginationItem = function(config){
5165     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5166     this.addEvents({
5167         // raw events
5168         /**
5169          * @event click
5170          * The raw click event for the entire grid.
5171          * @param {Roo.EventObject} e
5172          */
5173         "click" : true
5174     });
5175 };
5176
5177 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5178     
5179     href : false,
5180     html : false,
5181     preventDefault: true,
5182     active : false,
5183     cls : false,
5184     disabled: false,
5185     
5186     getAutoCreate : function(){
5187         var cfg= {
5188             tag: 'li',
5189             cn: [
5190                 {
5191                     tag : 'a',
5192                     href : this.href ? this.href : '#',
5193                     html : this.html ? this.html : ''
5194                 }
5195             ]
5196         };
5197         
5198         if(this.cls){
5199             cfg.cls = this.cls;
5200         }
5201         
5202         if(this.disabled){
5203             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5204         }
5205         
5206         if(this.active){
5207             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5208         }
5209         
5210         return cfg;
5211     },
5212     
5213     initEvents: function() {
5214         
5215         this.el.on('click', this.onClick, this);
5216         
5217     },
5218     onClick : function(e)
5219     {
5220         Roo.log('PaginationItem on click ');
5221         if(this.preventDefault){
5222             e.preventDefault();
5223         }
5224         
5225         if(this.disabled){
5226             return;
5227         }
5228         
5229         this.fireEvent('click', this, e);
5230     }
5231    
5232 });
5233
5234  
5235
5236  /*
5237  * - LGPL
5238  *
5239  * slider
5240  * 
5241  */
5242
5243
5244 /**
5245  * @class Roo.bootstrap.Slider
5246  * @extends Roo.bootstrap.Component
5247  * Bootstrap Slider class
5248  *    
5249  * @constructor
5250  * Create a new Slider
5251  * @param {Object} config The config object
5252  */
5253
5254 Roo.bootstrap.Slider = function(config){
5255     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5256 };
5257
5258 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5259     
5260     getAutoCreate : function(){
5261         
5262         var cfg = {
5263             tag: 'div',
5264             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5265             cn: [
5266                 {
5267                     tag: 'a',
5268                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5269                 }
5270             ]
5271         };
5272         
5273         return cfg;
5274     }
5275    
5276 });
5277
5278  /*
5279  * Based on:
5280  * Ext JS Library 1.1.1
5281  * Copyright(c) 2006-2007, Ext JS, LLC.
5282  *
5283  * Originally Released Under LGPL - original licence link has changed is not relivant.
5284  *
5285  * Fork - LGPL
5286  * <script type="text/javascript">
5287  */
5288  
5289
5290 /**
5291  * @class Roo.grid.ColumnModel
5292  * @extends Roo.util.Observable
5293  * This is the default implementation of a ColumnModel used by the Grid. It defines
5294  * the columns in the grid.
5295  * <br>Usage:<br>
5296  <pre><code>
5297  var colModel = new Roo.grid.ColumnModel([
5298         {header: "Ticker", width: 60, sortable: true, locked: true},
5299         {header: "Company Name", width: 150, sortable: true},
5300         {header: "Market Cap.", width: 100, sortable: true},
5301         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5302         {header: "Employees", width: 100, sortable: true, resizable: false}
5303  ]);
5304  </code></pre>
5305  * <p>
5306  
5307  * The config options listed for this class are options which may appear in each
5308  * individual column definition.
5309  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5310  * @constructor
5311  * @param {Object} config An Array of column config objects. See this class's
5312  * config objects for details.
5313 */
5314 Roo.grid.ColumnModel = function(config){
5315         /**
5316      * The config passed into the constructor
5317      */
5318     this.config = config;
5319     this.lookup = {};
5320
5321     // if no id, create one
5322     // if the column does not have a dataIndex mapping,
5323     // map it to the order it is in the config
5324     for(var i = 0, len = config.length; i < len; i++){
5325         var c = config[i];
5326         if(typeof c.dataIndex == "undefined"){
5327             c.dataIndex = i;
5328         }
5329         if(typeof c.renderer == "string"){
5330             c.renderer = Roo.util.Format[c.renderer];
5331         }
5332         if(typeof c.id == "undefined"){
5333             c.id = Roo.id();
5334         }
5335         if(c.editor && c.editor.xtype){
5336             c.editor  = Roo.factory(c.editor, Roo.grid);
5337         }
5338         if(c.editor && c.editor.isFormField){
5339             c.editor = new Roo.grid.GridEditor(c.editor);
5340         }
5341         this.lookup[c.id] = c;
5342     }
5343
5344     /**
5345      * The width of columns which have no width specified (defaults to 100)
5346      * @type Number
5347      */
5348     this.defaultWidth = 100;
5349
5350     /**
5351      * Default sortable of columns which have no sortable specified (defaults to false)
5352      * @type Boolean
5353      */
5354     this.defaultSortable = false;
5355
5356     this.addEvents({
5357         /**
5358              * @event widthchange
5359              * Fires when the width of a column changes.
5360              * @param {ColumnModel} this
5361              * @param {Number} columnIndex The column index
5362              * @param {Number} newWidth The new width
5363              */
5364             "widthchange": true,
5365         /**
5366              * @event headerchange
5367              * Fires when the text of a header changes.
5368              * @param {ColumnModel} this
5369              * @param {Number} columnIndex The column index
5370              * @param {Number} newText The new header text
5371              */
5372             "headerchange": true,
5373         /**
5374              * @event hiddenchange
5375              * Fires when a column is hidden or "unhidden".
5376              * @param {ColumnModel} this
5377              * @param {Number} columnIndex The column index
5378              * @param {Boolean} hidden true if hidden, false otherwise
5379              */
5380             "hiddenchange": true,
5381             /**
5382          * @event columnmoved
5383          * Fires when a column is moved.
5384          * @param {ColumnModel} this
5385          * @param {Number} oldIndex
5386          * @param {Number} newIndex
5387          */
5388         "columnmoved" : true,
5389         /**
5390          * @event columlockchange
5391          * Fires when a column's locked state is changed
5392          * @param {ColumnModel} this
5393          * @param {Number} colIndex
5394          * @param {Boolean} locked true if locked
5395          */
5396         "columnlockchange" : true
5397     });
5398     Roo.grid.ColumnModel.superclass.constructor.call(this);
5399 };
5400 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5401     /**
5402      * @cfg {String} header The header text to display in the Grid view.
5403      */
5404     /**
5405      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5406      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5407      * specified, the column's index is used as an index into the Record's data Array.
5408      */
5409     /**
5410      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5411      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5412      */
5413     /**
5414      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5415      * Defaults to the value of the {@link #defaultSortable} property.
5416      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5417      */
5418     /**
5419      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5420      */
5421     /**
5422      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5423      */
5424     /**
5425      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5426      */
5427     /**
5428      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5429      */
5430     /**
5431      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5432      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5433      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5434      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5435      */
5436        /**
5437      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5438      */
5439     /**
5440      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5441      */
5442     /**
5443      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5444      */
5445     /**
5446      * @cfg {String} cursor (Optional)
5447      */
5448     /**
5449      * @cfg {String} tooltip (Optional)
5450      */
5451     /**
5452      * @cfg {Number} xs (Optional)
5453      */
5454     /**
5455      * @cfg {Number} sm (Optional)
5456      */
5457     /**
5458      * @cfg {Number} md (Optional)
5459      */
5460     /**
5461      * @cfg {Number} lg (Optional)
5462      */
5463     /**
5464      * Returns the id of the column at the specified index.
5465      * @param {Number} index The column index
5466      * @return {String} the id
5467      */
5468     getColumnId : function(index){
5469         return this.config[index].id;
5470     },
5471
5472     /**
5473      * Returns the column for a specified id.
5474      * @param {String} id The column id
5475      * @return {Object} the column
5476      */
5477     getColumnById : function(id){
5478         return this.lookup[id];
5479     },
5480
5481     
5482     /**
5483      * Returns the column for a specified dataIndex.
5484      * @param {String} dataIndex The column dataIndex
5485      * @return {Object|Boolean} the column or false if not found
5486      */
5487     getColumnByDataIndex: function(dataIndex){
5488         var index = this.findColumnIndex(dataIndex);
5489         return index > -1 ? this.config[index] : false;
5490     },
5491     
5492     /**
5493      * Returns the index for a specified column id.
5494      * @param {String} id The column id
5495      * @return {Number} the index, or -1 if not found
5496      */
5497     getIndexById : function(id){
5498         for(var i = 0, len = this.config.length; i < len; i++){
5499             if(this.config[i].id == id){
5500                 return i;
5501             }
5502         }
5503         return -1;
5504     },
5505     
5506     /**
5507      * Returns the index for a specified column dataIndex.
5508      * @param {String} dataIndex The column dataIndex
5509      * @return {Number} the index, or -1 if not found
5510      */
5511     
5512     findColumnIndex : function(dataIndex){
5513         for(var i = 0, len = this.config.length; i < len; i++){
5514             if(this.config[i].dataIndex == dataIndex){
5515                 return i;
5516             }
5517         }
5518         return -1;
5519     },
5520     
5521     
5522     moveColumn : function(oldIndex, newIndex){
5523         var c = this.config[oldIndex];
5524         this.config.splice(oldIndex, 1);
5525         this.config.splice(newIndex, 0, c);
5526         this.dataMap = null;
5527         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5528     },
5529
5530     isLocked : function(colIndex){
5531         return this.config[colIndex].locked === true;
5532     },
5533
5534     setLocked : function(colIndex, value, suppressEvent){
5535         if(this.isLocked(colIndex) == value){
5536             return;
5537         }
5538         this.config[colIndex].locked = value;
5539         if(!suppressEvent){
5540             this.fireEvent("columnlockchange", this, colIndex, value);
5541         }
5542     },
5543
5544     getTotalLockedWidth : function(){
5545         var totalWidth = 0;
5546         for(var i = 0; i < this.config.length; i++){
5547             if(this.isLocked(i) && !this.isHidden(i)){
5548                 this.totalWidth += this.getColumnWidth(i);
5549             }
5550         }
5551         return totalWidth;
5552     },
5553
5554     getLockedCount : function(){
5555         for(var i = 0, len = this.config.length; i < len; i++){
5556             if(!this.isLocked(i)){
5557                 return i;
5558             }
5559         }
5560         
5561         return this.config.length;
5562     },
5563
5564     /**
5565      * Returns the number of columns.
5566      * @return {Number}
5567      */
5568     getColumnCount : function(visibleOnly){
5569         if(visibleOnly === true){
5570             var c = 0;
5571             for(var i = 0, len = this.config.length; i < len; i++){
5572                 if(!this.isHidden(i)){
5573                     c++;
5574                 }
5575             }
5576             return c;
5577         }
5578         return this.config.length;
5579     },
5580
5581     /**
5582      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5583      * @param {Function} fn
5584      * @param {Object} scope (optional)
5585      * @return {Array} result
5586      */
5587     getColumnsBy : function(fn, scope){
5588         var r = [];
5589         for(var i = 0, len = this.config.length; i < len; i++){
5590             var c = this.config[i];
5591             if(fn.call(scope||this, c, i) === true){
5592                 r[r.length] = c;
5593             }
5594         }
5595         return r;
5596     },
5597
5598     /**
5599      * Returns true if the specified column is sortable.
5600      * @param {Number} col The column index
5601      * @return {Boolean}
5602      */
5603     isSortable : function(col){
5604         if(typeof this.config[col].sortable == "undefined"){
5605             return this.defaultSortable;
5606         }
5607         return this.config[col].sortable;
5608     },
5609
5610     /**
5611      * Returns the rendering (formatting) function defined for the column.
5612      * @param {Number} col The column index.
5613      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5614      */
5615     getRenderer : function(col){
5616         if(!this.config[col].renderer){
5617             return Roo.grid.ColumnModel.defaultRenderer;
5618         }
5619         return this.config[col].renderer;
5620     },
5621
5622     /**
5623      * Sets the rendering (formatting) function for a column.
5624      * @param {Number} col The column index
5625      * @param {Function} fn The function to use to process the cell's raw data
5626      * to return HTML markup for the grid view. The render function is called with
5627      * the following parameters:<ul>
5628      * <li>Data value.</li>
5629      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5630      * <li>css A CSS style string to apply to the table cell.</li>
5631      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5632      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5633      * <li>Row index</li>
5634      * <li>Column index</li>
5635      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5636      */
5637     setRenderer : function(col, fn){
5638         this.config[col].renderer = fn;
5639     },
5640
5641     /**
5642      * Returns the width for the specified column.
5643      * @param {Number} col The column index
5644      * @return {Number}
5645      */
5646     getColumnWidth : function(col){
5647         return this.config[col].width * 1 || this.defaultWidth;
5648     },
5649
5650     /**
5651      * Sets the width for a column.
5652      * @param {Number} col The column index
5653      * @param {Number} width The new width
5654      */
5655     setColumnWidth : function(col, width, suppressEvent){
5656         this.config[col].width = width;
5657         this.totalWidth = null;
5658         if(!suppressEvent){
5659              this.fireEvent("widthchange", this, col, width);
5660         }
5661     },
5662
5663     /**
5664      * Returns the total width of all columns.
5665      * @param {Boolean} includeHidden True to include hidden column widths
5666      * @return {Number}
5667      */
5668     getTotalWidth : function(includeHidden){
5669         if(!this.totalWidth){
5670             this.totalWidth = 0;
5671             for(var i = 0, len = this.config.length; i < len; i++){
5672                 if(includeHidden || !this.isHidden(i)){
5673                     this.totalWidth += this.getColumnWidth(i);
5674                 }
5675             }
5676         }
5677         return this.totalWidth;
5678     },
5679
5680     /**
5681      * Returns the header for the specified column.
5682      * @param {Number} col The column index
5683      * @return {String}
5684      */
5685     getColumnHeader : function(col){
5686         return this.config[col].header;
5687     },
5688
5689     /**
5690      * Sets the header for a column.
5691      * @param {Number} col The column index
5692      * @param {String} header The new header
5693      */
5694     setColumnHeader : function(col, header){
5695         this.config[col].header = header;
5696         this.fireEvent("headerchange", this, col, header);
5697     },
5698
5699     /**
5700      * Returns the tooltip for the specified column.
5701      * @param {Number} col The column index
5702      * @return {String}
5703      */
5704     getColumnTooltip : function(col){
5705             return this.config[col].tooltip;
5706     },
5707     /**
5708      * Sets the tooltip for a column.
5709      * @param {Number} col The column index
5710      * @param {String} tooltip The new tooltip
5711      */
5712     setColumnTooltip : function(col, tooltip){
5713             this.config[col].tooltip = tooltip;
5714     },
5715
5716     /**
5717      * Returns the dataIndex for the specified column.
5718      * @param {Number} col The column index
5719      * @return {Number}
5720      */
5721     getDataIndex : function(col){
5722         return this.config[col].dataIndex;
5723     },
5724
5725     /**
5726      * Sets the dataIndex for a column.
5727      * @param {Number} col The column index
5728      * @param {Number} dataIndex The new dataIndex
5729      */
5730     setDataIndex : function(col, dataIndex){
5731         this.config[col].dataIndex = dataIndex;
5732     },
5733
5734     
5735     
5736     /**
5737      * Returns true if the cell is editable.
5738      * @param {Number} colIndex The column index
5739      * @param {Number} rowIndex The row index - this is nto actually used..?
5740      * @return {Boolean}
5741      */
5742     isCellEditable : function(colIndex, rowIndex){
5743         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5744     },
5745
5746     /**
5747      * Returns the editor defined for the cell/column.
5748      * return false or null to disable editing.
5749      * @param {Number} colIndex The column index
5750      * @param {Number} rowIndex The row index
5751      * @return {Object}
5752      */
5753     getCellEditor : function(colIndex, rowIndex){
5754         return this.config[colIndex].editor;
5755     },
5756
5757     /**
5758      * Sets if a column is editable.
5759      * @param {Number} col The column index
5760      * @param {Boolean} editable True if the column is editable
5761      */
5762     setEditable : function(col, editable){
5763         this.config[col].editable = editable;
5764     },
5765
5766
5767     /**
5768      * Returns true if the column is hidden.
5769      * @param {Number} colIndex The column index
5770      * @return {Boolean}
5771      */
5772     isHidden : function(colIndex){
5773         return this.config[colIndex].hidden;
5774     },
5775
5776
5777     /**
5778      * Returns true if the column width cannot be changed
5779      */
5780     isFixed : function(colIndex){
5781         return this.config[colIndex].fixed;
5782     },
5783
5784     /**
5785      * Returns true if the column can be resized
5786      * @return {Boolean}
5787      */
5788     isResizable : function(colIndex){
5789         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5790     },
5791     /**
5792      * Sets if a column is hidden.
5793      * @param {Number} colIndex The column index
5794      * @param {Boolean} hidden True if the column is hidden
5795      */
5796     setHidden : function(colIndex, hidden){
5797         this.config[colIndex].hidden = hidden;
5798         this.totalWidth = null;
5799         this.fireEvent("hiddenchange", this, colIndex, hidden);
5800     },
5801
5802     /**
5803      * Sets the editor for a column.
5804      * @param {Number} col The column index
5805      * @param {Object} editor The editor object
5806      */
5807     setEditor : function(col, editor){
5808         this.config[col].editor = editor;
5809     }
5810 });
5811
5812 Roo.grid.ColumnModel.defaultRenderer = function(value)
5813 {
5814     if(typeof value == "object") {
5815         return value;
5816     }
5817         if(typeof value == "string" && value.length < 1){
5818             return "&#160;";
5819         }
5820     
5821         return String.format("{0}", value);
5822 };
5823
5824 // Alias for backwards compatibility
5825 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5826 /*
5827  * Based on:
5828  * Ext JS Library 1.1.1
5829  * Copyright(c) 2006-2007, Ext JS, LLC.
5830  *
5831  * Originally Released Under LGPL - original licence link has changed is not relivant.
5832  *
5833  * Fork - LGPL
5834  * <script type="text/javascript">
5835  */
5836  
5837 /**
5838  * @class Roo.LoadMask
5839  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5840  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5841  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5842  * element's UpdateManager load indicator and will be destroyed after the initial load.
5843  * @constructor
5844  * Create a new LoadMask
5845  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5846  * @param {Object} config The config object
5847  */
5848 Roo.LoadMask = function(el, config){
5849     this.el = Roo.get(el);
5850     Roo.apply(this, config);
5851     if(this.store){
5852         this.store.on('beforeload', this.onBeforeLoad, this);
5853         this.store.on('load', this.onLoad, this);
5854         this.store.on('loadexception', this.onLoadException, this);
5855         this.removeMask = false;
5856     }else{
5857         var um = this.el.getUpdateManager();
5858         um.showLoadIndicator = false; // disable the default indicator
5859         um.on('beforeupdate', this.onBeforeLoad, this);
5860         um.on('update', this.onLoad, this);
5861         um.on('failure', this.onLoad, this);
5862         this.removeMask = true;
5863     }
5864 };
5865
5866 Roo.LoadMask.prototype = {
5867     /**
5868      * @cfg {Boolean} removeMask
5869      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5870      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5871      */
5872     /**
5873      * @cfg {String} msg
5874      * The text to display in a centered loading message box (defaults to 'Loading...')
5875      */
5876     msg : 'Loading...',
5877     /**
5878      * @cfg {String} msgCls
5879      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5880      */
5881     msgCls : 'x-mask-loading',
5882
5883     /**
5884      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5885      * @type Boolean
5886      */
5887     disabled: false,
5888
5889     /**
5890      * Disables the mask to prevent it from being displayed
5891      */
5892     disable : function(){
5893        this.disabled = true;
5894     },
5895
5896     /**
5897      * Enables the mask so that it can be displayed
5898      */
5899     enable : function(){
5900         this.disabled = false;
5901     },
5902     
5903     onLoadException : function()
5904     {
5905         Roo.log(arguments);
5906         
5907         if (typeof(arguments[3]) != 'undefined') {
5908             Roo.MessageBox.alert("Error loading",arguments[3]);
5909         } 
5910         /*
5911         try {
5912             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5913                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5914             }   
5915         } catch(e) {
5916             
5917         }
5918         */
5919     
5920         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5921     },
5922     // private
5923     onLoad : function()
5924     {
5925         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5926     },
5927
5928     // private
5929     onBeforeLoad : function(){
5930         if(!this.disabled){
5931             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5932         }
5933     },
5934
5935     // private
5936     destroy : function(){
5937         if(this.store){
5938             this.store.un('beforeload', this.onBeforeLoad, this);
5939             this.store.un('load', this.onLoad, this);
5940             this.store.un('loadexception', this.onLoadException, this);
5941         }else{
5942             var um = this.el.getUpdateManager();
5943             um.un('beforeupdate', this.onBeforeLoad, this);
5944             um.un('update', this.onLoad, this);
5945             um.un('failure', this.onLoad, this);
5946         }
5947     }
5948 };/*
5949  * - LGPL
5950  *
5951  * table
5952  * 
5953  */
5954
5955 /**
5956  * @class Roo.bootstrap.Table
5957  * @extends Roo.bootstrap.Component
5958  * Bootstrap Table class
5959  * @cfg {String} cls table class
5960  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5961  * @cfg {String} bgcolor Specifies the background color for a table
5962  * @cfg {Number} border Specifies whether the table cells should have borders or not
5963  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5964  * @cfg {Number} cellspacing Specifies the space between cells
5965  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5966  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5967  * @cfg {String} sortable Specifies that the table should be sortable
5968  * @cfg {String} summary Specifies a summary of the content of a table
5969  * @cfg {Number} width Specifies the width of a table
5970  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5971  * 
5972  * @cfg {boolean} striped Should the rows be alternative striped
5973  * @cfg {boolean} bordered Add borders to the table
5974  * @cfg {boolean} hover Add hover highlighting
5975  * @cfg {boolean} condensed Format condensed
5976  * @cfg {boolean} responsive Format condensed
5977  * @cfg {Boolean} loadMask (true|false) default false
5978  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5979  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5980  * @cfg {Boolean} rowSelection (true|false) default false
5981  * @cfg {Boolean} cellSelection (true|false) default false
5982  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5983  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5984  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5985  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5986  
5987  * 
5988  * @constructor
5989  * Create a new Table
5990  * @param {Object} config The config object
5991  */
5992
5993 Roo.bootstrap.Table = function(config){
5994     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5995     
5996   
5997     
5998     // BC...
5999     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6000     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6001     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6002     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6003     
6004     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6005     if (this.sm) {
6006         this.sm.grid = this;
6007         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6008         this.sm = this.selModel;
6009         this.sm.xmodule = this.xmodule || false;
6010     }
6011     
6012     if (this.cm && typeof(this.cm.config) == 'undefined') {
6013         this.colModel = new Roo.grid.ColumnModel(this.cm);
6014         this.cm = this.colModel;
6015         this.cm.xmodule = this.xmodule || false;
6016     }
6017     if (this.store) {
6018         this.store= Roo.factory(this.store, Roo.data);
6019         this.ds = this.store;
6020         this.ds.xmodule = this.xmodule || false;
6021          
6022     }
6023     if (this.footer && this.store) {
6024         this.footer.dataSource = this.ds;
6025         this.footer = Roo.factory(this.footer);
6026     }
6027     
6028     /** @private */
6029     this.addEvents({
6030         /**
6031          * @event cellclick
6032          * Fires when a cell is clicked
6033          * @param {Roo.bootstrap.Table} this
6034          * @param {Roo.Element} el
6035          * @param {Number} rowIndex
6036          * @param {Number} columnIndex
6037          * @param {Roo.EventObject} e
6038          */
6039         "cellclick" : true,
6040         /**
6041          * @event celldblclick
6042          * Fires when a cell is double clicked
6043          * @param {Roo.bootstrap.Table} this
6044          * @param {Roo.Element} el
6045          * @param {Number} rowIndex
6046          * @param {Number} columnIndex
6047          * @param {Roo.EventObject} e
6048          */
6049         "celldblclick" : true,
6050         /**
6051          * @event rowclick
6052          * Fires when a row is clicked
6053          * @param {Roo.bootstrap.Table} this
6054          * @param {Roo.Element} el
6055          * @param {Number} rowIndex
6056          * @param {Roo.EventObject} e
6057          */
6058         "rowclick" : true,
6059         /**
6060          * @event rowdblclick
6061          * Fires when a row is double clicked
6062          * @param {Roo.bootstrap.Table} this
6063          * @param {Roo.Element} el
6064          * @param {Number} rowIndex
6065          * @param {Roo.EventObject} e
6066          */
6067         "rowdblclick" : true,
6068         /**
6069          * @event mouseover
6070          * Fires when a mouseover occur
6071          * @param {Roo.bootstrap.Table} this
6072          * @param {Roo.Element} el
6073          * @param {Number} rowIndex
6074          * @param {Number} columnIndex
6075          * @param {Roo.EventObject} e
6076          */
6077         "mouseover" : true,
6078         /**
6079          * @event mouseout
6080          * Fires when a mouseout occur
6081          * @param {Roo.bootstrap.Table} this
6082          * @param {Roo.Element} el
6083          * @param {Number} rowIndex
6084          * @param {Number} columnIndex
6085          * @param {Roo.EventObject} e
6086          */
6087         "mouseout" : true,
6088         /**
6089          * @event rowclass
6090          * Fires when a row is rendered, so you can change add a style to it.
6091          * @param {Roo.bootstrap.Table} this
6092          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6093          */
6094         'rowclass' : true,
6095           /**
6096          * @event rowsrendered
6097          * Fires when all the  rows have been rendered
6098          * @param {Roo.bootstrap.Table} this
6099          */
6100         'rowsrendered' : true,
6101         /**
6102          * @event contextmenu
6103          * The raw contextmenu event for the entire grid.
6104          * @param {Roo.EventObject} e
6105          */
6106         "contextmenu" : true,
6107         /**
6108          * @event rowcontextmenu
6109          * Fires when a row is right clicked
6110          * @param {Roo.bootstrap.Table} this
6111          * @param {Number} rowIndex
6112          * @param {Roo.EventObject} e
6113          */
6114         "rowcontextmenu" : true,
6115         /**
6116          * @event cellcontextmenu
6117          * Fires when a cell is right clicked
6118          * @param {Roo.bootstrap.Table} this
6119          * @param {Number} rowIndex
6120          * @param {Number} cellIndex
6121          * @param {Roo.EventObject} e
6122          */
6123          "cellcontextmenu" : true,
6124          /**
6125          * @event headercontextmenu
6126          * Fires when a header is right clicked
6127          * @param {Roo.bootstrap.Table} this
6128          * @param {Number} columnIndex
6129          * @param {Roo.EventObject} e
6130          */
6131         "headercontextmenu" : true
6132     });
6133 };
6134
6135 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6136     
6137     cls: false,
6138     align: false,
6139     bgcolor: false,
6140     border: false,
6141     cellpadding: false,
6142     cellspacing: false,
6143     frame: false,
6144     rules: false,
6145     sortable: false,
6146     summary: false,
6147     width: false,
6148     striped : false,
6149     scrollBody : false,
6150     bordered: false,
6151     hover:  false,
6152     condensed : false,
6153     responsive : false,
6154     sm : false,
6155     cm : false,
6156     store : false,
6157     loadMask : false,
6158     footerShow : true,
6159     headerShow : true,
6160   
6161     rowSelection : false,
6162     cellSelection : false,
6163     layout : false,
6164     
6165     // Roo.Element - the tbody
6166     mainBody: false,
6167     // Roo.Element - thead element
6168     mainHead: false,
6169     
6170     container: false, // used by gridpanel...
6171     
6172     lazyLoad : false,
6173     
6174     CSS : Roo.util.CSS,
6175     
6176     auto_hide_footer : false,
6177     
6178     getAutoCreate : function()
6179     {
6180         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6181         
6182         cfg = {
6183             tag: 'table',
6184             cls : 'table',
6185             cn : []
6186         };
6187         if (this.scrollBody) {
6188             cfg.cls += ' table-body-fixed';
6189         }    
6190         if (this.striped) {
6191             cfg.cls += ' table-striped';
6192         }
6193         
6194         if (this.hover) {
6195             cfg.cls += ' table-hover';
6196         }
6197         if (this.bordered) {
6198             cfg.cls += ' table-bordered';
6199         }
6200         if (this.condensed) {
6201             cfg.cls += ' table-condensed';
6202         }
6203         if (this.responsive) {
6204             cfg.cls += ' table-responsive';
6205         }
6206         
6207         if (this.cls) {
6208             cfg.cls+=  ' ' +this.cls;
6209         }
6210         
6211         // this lot should be simplifed...
6212         var _t = this;
6213         var cp = [
6214             'align',
6215             'bgcolor',
6216             'border',
6217             'cellpadding',
6218             'cellspacing',
6219             'frame',
6220             'rules',
6221             'sortable',
6222             'summary',
6223             'width'
6224         ].forEach(function(k) {
6225             if (_t[k]) {
6226                 cfg[k] = _t[k];
6227             }
6228         });
6229         
6230         
6231         if (this.layout) {
6232             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6233         }
6234         
6235         if(this.store || this.cm){
6236             if(this.headerShow){
6237                 cfg.cn.push(this.renderHeader());
6238             }
6239             
6240             cfg.cn.push(this.renderBody());
6241             
6242             if(this.footerShow){
6243                 cfg.cn.push(this.renderFooter());
6244             }
6245             // where does this come from?
6246             //cfg.cls+=  ' TableGrid';
6247         }
6248         
6249         return { cn : [ cfg ] };
6250     },
6251     
6252     initEvents : function()
6253     {   
6254         if(!this.store || !this.cm){
6255             return;
6256         }
6257         if (this.selModel) {
6258             this.selModel.initEvents();
6259         }
6260         
6261         
6262         //Roo.log('initEvents with ds!!!!');
6263         
6264         this.mainBody = this.el.select('tbody', true).first();
6265         this.mainHead = this.el.select('thead', true).first();
6266         this.mainFoot = this.el.select('tfoot', true).first();
6267         
6268         
6269         
6270         var _this = this;
6271         
6272         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6273             e.on('click', _this.sort, _this);
6274         });
6275         
6276         this.mainBody.on("click", this.onClick, this);
6277         this.mainBody.on("dblclick", this.onDblClick, this);
6278         
6279         // why is this done????? = it breaks dialogs??
6280         //this.parent().el.setStyle('position', 'relative');
6281         
6282         
6283         if (this.footer) {
6284             this.footer.parentId = this.id;
6285             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6286             
6287             if(this.lazyLoad){
6288                 this.el.select('tfoot tr td').first().addClass('hide');
6289             }
6290         } 
6291         
6292         if(this.loadMask) {
6293             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6294         }
6295         
6296         this.store.on('load', this.onLoad, this);
6297         this.store.on('beforeload', this.onBeforeLoad, this);
6298         this.store.on('update', this.onUpdate, this);
6299         this.store.on('add', this.onAdd, this);
6300         this.store.on("clear", this.clear, this);
6301         
6302         this.el.on("contextmenu", this.onContextMenu, this);
6303         
6304         this.mainBody.on('scroll', this.onBodyScroll, this);
6305         
6306         this.cm.on("headerchange", this.onHeaderChange, this);
6307         
6308         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6309         
6310     },
6311     
6312     onContextMenu : function(e, t)
6313     {
6314         this.processEvent("contextmenu", e);
6315     },
6316     
6317     processEvent : function(name, e)
6318     {
6319         if (name != 'touchstart' ) {
6320             this.fireEvent(name, e);    
6321         }
6322         
6323         var t = e.getTarget();
6324         
6325         var cell = Roo.get(t);
6326         
6327         if(!cell){
6328             return;
6329         }
6330         
6331         if(cell.findParent('tfoot', false, true)){
6332             return;
6333         }
6334         
6335         if(cell.findParent('thead', false, true)){
6336             
6337             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6338                 cell = Roo.get(t).findParent('th', false, true);
6339                 if (!cell) {
6340                     Roo.log("failed to find th in thead?");
6341                     Roo.log(e.getTarget());
6342                     return;
6343                 }
6344             }
6345             
6346             var cellIndex = cell.dom.cellIndex;
6347             
6348             var ename = name == 'touchstart' ? 'click' : name;
6349             this.fireEvent("header" + ename, this, cellIndex, e);
6350             
6351             return;
6352         }
6353         
6354         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6355             cell = Roo.get(t).findParent('td', false, true);
6356             if (!cell) {
6357                 Roo.log("failed to find th in tbody?");
6358                 Roo.log(e.getTarget());
6359                 return;
6360             }
6361         }
6362         
6363         var row = cell.findParent('tr', false, true);
6364         var cellIndex = cell.dom.cellIndex;
6365         var rowIndex = row.dom.rowIndex - 1;
6366         
6367         if(row !== false){
6368             
6369             this.fireEvent("row" + name, this, rowIndex, e);
6370             
6371             if(cell !== false){
6372             
6373                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6374             }
6375         }
6376         
6377     },
6378     
6379     onMouseover : function(e, el)
6380     {
6381         var cell = Roo.get(el);
6382         
6383         if(!cell){
6384             return;
6385         }
6386         
6387         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6388             cell = cell.findParent('td', false, true);
6389         }
6390         
6391         var row = cell.findParent('tr', false, true);
6392         var cellIndex = cell.dom.cellIndex;
6393         var rowIndex = row.dom.rowIndex - 1; // start from 0
6394         
6395         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6396         
6397     },
6398     
6399     onMouseout : function(e, el)
6400     {
6401         var cell = Roo.get(el);
6402         
6403         if(!cell){
6404             return;
6405         }
6406         
6407         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6408             cell = cell.findParent('td', false, true);
6409         }
6410         
6411         var row = cell.findParent('tr', false, true);
6412         var cellIndex = cell.dom.cellIndex;
6413         var rowIndex = row.dom.rowIndex - 1; // start from 0
6414         
6415         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6416         
6417     },
6418     
6419     onClick : function(e, el)
6420     {
6421         var cell = Roo.get(el);
6422         
6423         if(!cell || (!this.cellSelection && !this.rowSelection)){
6424             return;
6425         }
6426         
6427         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6428             cell = cell.findParent('td', false, true);
6429         }
6430         
6431         if(!cell || typeof(cell) == 'undefined'){
6432             return;
6433         }
6434         
6435         var row = cell.findParent('tr', false, true);
6436         
6437         if(!row || typeof(row) == 'undefined'){
6438             return;
6439         }
6440         
6441         var cellIndex = cell.dom.cellIndex;
6442         var rowIndex = this.getRowIndex(row);
6443         
6444         // why??? - should these not be based on SelectionModel?
6445         if(this.cellSelection){
6446             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6447         }
6448         
6449         if(this.rowSelection){
6450             this.fireEvent('rowclick', this, row, rowIndex, e);
6451         }
6452         
6453         
6454     },
6455         
6456     onDblClick : function(e,el)
6457     {
6458         var cell = Roo.get(el);
6459         
6460         if(!cell || (!this.cellSelection && !this.rowSelection)){
6461             return;
6462         }
6463         
6464         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6465             cell = cell.findParent('td', false, true);
6466         }
6467         
6468         if(!cell || typeof(cell) == 'undefined'){
6469             return;
6470         }
6471         
6472         var row = cell.findParent('tr', false, true);
6473         
6474         if(!row || typeof(row) == 'undefined'){
6475             return;
6476         }
6477         
6478         var cellIndex = cell.dom.cellIndex;
6479         var rowIndex = this.getRowIndex(row);
6480         
6481         if(this.cellSelection){
6482             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6483         }
6484         
6485         if(this.rowSelection){
6486             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6487         }
6488     },
6489     
6490     sort : function(e,el)
6491     {
6492         var col = Roo.get(el);
6493         
6494         if(!col.hasClass('sortable')){
6495             return;
6496         }
6497         
6498         var sort = col.attr('sort');
6499         var dir = 'ASC';
6500         
6501         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6502             dir = 'DESC';
6503         }
6504         
6505         this.store.sortInfo = {field : sort, direction : dir};
6506         
6507         if (this.footer) {
6508             Roo.log("calling footer first");
6509             this.footer.onClick('first');
6510         } else {
6511         
6512             this.store.load({ params : { start : 0 } });
6513         }
6514     },
6515     
6516     renderHeader : function()
6517     {
6518         var header = {
6519             tag: 'thead',
6520             cn : []
6521         };
6522         
6523         var cm = this.cm;
6524         this.totalWidth = 0;
6525         
6526         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6527             
6528             var config = cm.config[i];
6529             
6530             var c = {
6531                 tag: 'th',
6532                 cls : 'x-hcol-' + i,
6533                 style : '',
6534                 html: cm.getColumnHeader(i)
6535             };
6536             
6537             var hh = '';
6538             
6539             if(typeof(config.sortable) != 'undefined' && config.sortable){
6540                 c.cls = 'sortable';
6541                 c.html = '<i class="glyphicon"></i>' + c.html;
6542             }
6543             
6544             if(typeof(config.lgHeader) != 'undefined'){
6545                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6546             }
6547             
6548             if(typeof(config.mdHeader) != 'undefined'){
6549                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6550             }
6551             
6552             if(typeof(config.smHeader) != 'undefined'){
6553                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6554             }
6555             
6556             if(typeof(config.xsHeader) != 'undefined'){
6557                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6558             }
6559             
6560             if(hh.length){
6561                 c.html = hh;
6562             }
6563             
6564             if(typeof(config.tooltip) != 'undefined'){
6565                 c.tooltip = config.tooltip;
6566             }
6567             
6568             if(typeof(config.colspan) != 'undefined'){
6569                 c.colspan = config.colspan;
6570             }
6571             
6572             if(typeof(config.hidden) != 'undefined' && config.hidden){
6573                 c.style += ' display:none;';
6574             }
6575             
6576             if(typeof(config.dataIndex) != 'undefined'){
6577                 c.sort = config.dataIndex;
6578             }
6579             
6580            
6581             
6582             if(typeof(config.align) != 'undefined' && config.align.length){
6583                 c.style += ' text-align:' + config.align + ';';
6584             }
6585             
6586             if(typeof(config.width) != 'undefined'){
6587                 c.style += ' width:' + config.width + 'px;';
6588                 this.totalWidth += config.width;
6589             } else {
6590                 this.totalWidth += 100; // assume minimum of 100 per column?
6591             }
6592             
6593             if(typeof(config.cls) != 'undefined'){
6594                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6595             }
6596             
6597             ['xs','sm','md','lg'].map(function(size){
6598                 
6599                 if(typeof(config[size]) == 'undefined'){
6600                     return;
6601                 }
6602                 
6603                 if (!config[size]) { // 0 = hidden
6604                     c.cls += ' hidden-' + size;
6605                     return;
6606                 }
6607                 
6608                 c.cls += ' col-' + size + '-' + config[size];
6609
6610             });
6611             
6612             header.cn.push(c)
6613         }
6614         
6615         return header;
6616     },
6617     
6618     renderBody : function()
6619     {
6620         var body = {
6621             tag: 'tbody',
6622             cn : [
6623                 {
6624                     tag: 'tr',
6625                     cn : [
6626                         {
6627                             tag : 'td',
6628                             colspan :  this.cm.getColumnCount()
6629                         }
6630                     ]
6631                 }
6632             ]
6633         };
6634         
6635         return body;
6636     },
6637     
6638     renderFooter : function()
6639     {
6640         var footer = {
6641             tag: 'tfoot',
6642             cn : [
6643                 {
6644                     tag: 'tr',
6645                     cn : [
6646                         {
6647                             tag : 'td',
6648                             colspan :  this.cm.getColumnCount()
6649                         }
6650                     ]
6651                 }
6652             ]
6653         };
6654         
6655         return footer;
6656     },
6657     
6658     
6659     
6660     onLoad : function()
6661     {
6662 //        Roo.log('ds onload');
6663         this.clear();
6664         
6665         var _this = this;
6666         var cm = this.cm;
6667         var ds = this.store;
6668         
6669         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6670             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6671             if (_this.store.sortInfo) {
6672                     
6673                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6674                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6675                 }
6676                 
6677                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6678                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6679                 }
6680             }
6681         });
6682         
6683         var tbody =  this.mainBody;
6684               
6685         if(ds.getCount() > 0){
6686             ds.data.each(function(d,rowIndex){
6687                 var row =  this.renderRow(cm, ds, rowIndex);
6688                 
6689                 tbody.createChild(row);
6690                 
6691                 var _this = this;
6692                 
6693                 if(row.cellObjects.length){
6694                     Roo.each(row.cellObjects, function(r){
6695                         _this.renderCellObject(r);
6696                     })
6697                 }
6698                 
6699             }, this);
6700         }
6701         
6702         var tfoot = this.el.select('tfoot', true).first();
6703         
6704         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6705             
6706             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6707             
6708             var total = this.ds.getTotalCount();
6709             
6710             if(this.footer.pageSize < total){
6711                 this.mainFoot.show();
6712             }
6713         }
6714         
6715         Roo.each(this.el.select('tbody td', true).elements, function(e){
6716             e.on('mouseover', _this.onMouseover, _this);
6717         });
6718         
6719         Roo.each(this.el.select('tbody td', true).elements, function(e){
6720             e.on('mouseout', _this.onMouseout, _this);
6721         });
6722         this.fireEvent('rowsrendered', this);
6723         
6724         this.autoSize();
6725     },
6726     
6727     
6728     onUpdate : function(ds,record)
6729     {
6730         this.refreshRow(record);
6731         this.autoSize();
6732     },
6733     
6734     onRemove : function(ds, record, index, isUpdate){
6735         if(isUpdate !== true){
6736             this.fireEvent("beforerowremoved", this, index, record);
6737         }
6738         var bt = this.mainBody.dom;
6739         
6740         var rows = this.el.select('tbody > tr', true).elements;
6741         
6742         if(typeof(rows[index]) != 'undefined'){
6743             bt.removeChild(rows[index].dom);
6744         }
6745         
6746 //        if(bt.rows[index]){
6747 //            bt.removeChild(bt.rows[index]);
6748 //        }
6749         
6750         if(isUpdate !== true){
6751             //this.stripeRows(index);
6752             //this.syncRowHeights(index, index);
6753             //this.layout();
6754             this.fireEvent("rowremoved", this, index, record);
6755         }
6756     },
6757     
6758     onAdd : function(ds, records, rowIndex)
6759     {
6760         //Roo.log('on Add called');
6761         // - note this does not handle multiple adding very well..
6762         var bt = this.mainBody.dom;
6763         for (var i =0 ; i < records.length;i++) {
6764             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6765             //Roo.log(records[i]);
6766             //Roo.log(this.store.getAt(rowIndex+i));
6767             this.insertRow(this.store, rowIndex + i, false);
6768             return;
6769         }
6770         
6771     },
6772     
6773     
6774     refreshRow : function(record){
6775         var ds = this.store, index;
6776         if(typeof record == 'number'){
6777             index = record;
6778             record = ds.getAt(index);
6779         }else{
6780             index = ds.indexOf(record);
6781         }
6782         this.insertRow(ds, index, true);
6783         this.autoSize();
6784         this.onRemove(ds, record, index+1, true);
6785         this.autoSize();
6786         //this.syncRowHeights(index, index);
6787         //this.layout();
6788         this.fireEvent("rowupdated", this, index, record);
6789     },
6790     
6791     insertRow : function(dm, rowIndex, isUpdate){
6792         
6793         if(!isUpdate){
6794             this.fireEvent("beforerowsinserted", this, rowIndex);
6795         }
6796             //var s = this.getScrollState();
6797         var row = this.renderRow(this.cm, this.store, rowIndex);
6798         // insert before rowIndex..
6799         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6800         
6801         var _this = this;
6802                 
6803         if(row.cellObjects.length){
6804             Roo.each(row.cellObjects, function(r){
6805                 _this.renderCellObject(r);
6806             })
6807         }
6808             
6809         if(!isUpdate){
6810             this.fireEvent("rowsinserted", this, rowIndex);
6811             //this.syncRowHeights(firstRow, lastRow);
6812             //this.stripeRows(firstRow);
6813             //this.layout();
6814         }
6815         
6816     },
6817     
6818     
6819     getRowDom : function(rowIndex)
6820     {
6821         var rows = this.el.select('tbody > tr', true).elements;
6822         
6823         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6824         
6825     },
6826     // returns the object tree for a tr..
6827   
6828     
6829     renderRow : function(cm, ds, rowIndex) 
6830     {
6831         var d = ds.getAt(rowIndex);
6832         
6833         var row = {
6834             tag : 'tr',
6835             cls : 'x-row-' + rowIndex,
6836             cn : []
6837         };
6838             
6839         var cellObjects = [];
6840         
6841         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6842             var config = cm.config[i];
6843             
6844             var renderer = cm.getRenderer(i);
6845             var value = '';
6846             var id = false;
6847             
6848             if(typeof(renderer) !== 'undefined'){
6849                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6850             }
6851             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6852             // and are rendered into the cells after the row is rendered - using the id for the element.
6853             
6854             if(typeof(value) === 'object'){
6855                 id = Roo.id();
6856                 cellObjects.push({
6857                     container : id,
6858                     cfg : value 
6859                 })
6860             }
6861             
6862             var rowcfg = {
6863                 record: d,
6864                 rowIndex : rowIndex,
6865                 colIndex : i,
6866                 rowClass : ''
6867             };
6868
6869             this.fireEvent('rowclass', this, rowcfg);
6870             
6871             var td = {
6872                 tag: 'td',
6873                 cls : rowcfg.rowClass + ' x-col-' + i,
6874                 style: '',
6875                 html: (typeof(value) === 'object') ? '' : value
6876             };
6877             
6878             if (id) {
6879                 td.id = id;
6880             }
6881             
6882             if(typeof(config.colspan) != 'undefined'){
6883                 td.colspan = config.colspan;
6884             }
6885             
6886             if(typeof(config.hidden) != 'undefined' && config.hidden){
6887                 td.style += ' display:none;';
6888             }
6889             
6890             if(typeof(config.align) != 'undefined' && config.align.length){
6891                 td.style += ' text-align:' + config.align + ';';
6892             }
6893             if(typeof(config.valign) != 'undefined' && config.valign.length){
6894                 td.style += ' vertical-align:' + config.valign + ';';
6895             }
6896             
6897             if(typeof(config.width) != 'undefined'){
6898                 td.style += ' width:' +  config.width + 'px;';
6899             }
6900             
6901             if(typeof(config.cursor) != 'undefined'){
6902                 td.style += ' cursor:' +  config.cursor + ';';
6903             }
6904             
6905             if(typeof(config.cls) != 'undefined'){
6906                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6907             }
6908             
6909             ['xs','sm','md','lg'].map(function(size){
6910                 
6911                 if(typeof(config[size]) == 'undefined'){
6912                     return;
6913                 }
6914                 
6915                 if (!config[size]) { // 0 = hidden
6916                     td.cls += ' hidden-' + size;
6917                     return;
6918                 }
6919                 
6920                 td.cls += ' col-' + size + '-' + config[size];
6921
6922             });
6923             
6924             row.cn.push(td);
6925            
6926         }
6927         
6928         row.cellObjects = cellObjects;
6929         
6930         return row;
6931           
6932     },
6933     
6934     
6935     
6936     onBeforeLoad : function()
6937     {
6938         
6939     },
6940      /**
6941      * Remove all rows
6942      */
6943     clear : function()
6944     {
6945         this.el.select('tbody', true).first().dom.innerHTML = '';
6946     },
6947     /**
6948      * Show or hide a row.
6949      * @param {Number} rowIndex to show or hide
6950      * @param {Boolean} state hide
6951      */
6952     setRowVisibility : function(rowIndex, state)
6953     {
6954         var bt = this.mainBody.dom;
6955         
6956         var rows = this.el.select('tbody > tr', true).elements;
6957         
6958         if(typeof(rows[rowIndex]) == 'undefined'){
6959             return;
6960         }
6961         rows[rowIndex].dom.style.display = state ? '' : 'none';
6962     },
6963     
6964     
6965     getSelectionModel : function(){
6966         if(!this.selModel){
6967             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6968         }
6969         return this.selModel;
6970     },
6971     /*
6972      * Render the Roo.bootstrap object from renderder
6973      */
6974     renderCellObject : function(r)
6975     {
6976         var _this = this;
6977         
6978         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6979         
6980         var t = r.cfg.render(r.container);
6981         
6982         if(r.cfg.cn){
6983             Roo.each(r.cfg.cn, function(c){
6984                 var child = {
6985                     container: t.getChildContainer(),
6986                     cfg: c
6987                 };
6988                 _this.renderCellObject(child);
6989             })
6990         }
6991     },
6992     
6993     getRowIndex : function(row)
6994     {
6995         var rowIndex = -1;
6996         
6997         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6998             if(el != row){
6999                 return;
7000             }
7001             
7002             rowIndex = index;
7003         });
7004         
7005         return rowIndex;
7006     },
7007      /**
7008      * Returns the grid's underlying element = used by panel.Grid
7009      * @return {Element} The element
7010      */
7011     getGridEl : function(){
7012         return this.el;
7013     },
7014      /**
7015      * Forces a resize - used by panel.Grid
7016      * @return {Element} The element
7017      */
7018     autoSize : function()
7019     {
7020         //var ctr = Roo.get(this.container.dom.parentElement);
7021         var ctr = Roo.get(this.el.dom);
7022         
7023         var thd = this.getGridEl().select('thead',true).first();
7024         var tbd = this.getGridEl().select('tbody', true).first();
7025         var tfd = this.getGridEl().select('tfoot', true).first();
7026         
7027         var cw = ctr.getWidth();
7028         
7029         if (tbd) {
7030             
7031             tbd.setSize(ctr.getWidth(),
7032                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7033             );
7034             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7035             cw -= barsize;
7036         }
7037         cw = Math.max(cw, this.totalWidth);
7038         this.getGridEl().select('tr',true).setWidth(cw);
7039         // resize 'expandable coloumn?
7040         
7041         return; // we doe not have a view in this design..
7042         
7043     },
7044     onBodyScroll: function()
7045     {
7046         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7047         if(this.mainHead){
7048             this.mainHead.setStyle({
7049                 'position' : 'relative',
7050                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7051             });
7052         }
7053         
7054         if(this.lazyLoad){
7055             
7056             var scrollHeight = this.mainBody.dom.scrollHeight;
7057             
7058             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7059             
7060             var height = this.mainBody.getHeight();
7061             
7062             if(scrollHeight - height == scrollTop) {
7063                 
7064                 var total = this.ds.getTotalCount();
7065                 
7066                 if(this.footer.cursor + this.footer.pageSize < total){
7067                     
7068                     this.footer.ds.load({
7069                         params : {
7070                             start : this.footer.cursor + this.footer.pageSize,
7071                             limit : this.footer.pageSize
7072                         },
7073                         add : true
7074                     });
7075                 }
7076             }
7077             
7078         }
7079     },
7080     
7081     onHeaderChange : function()
7082     {
7083         var header = this.renderHeader();
7084         var table = this.el.select('table', true).first();
7085         
7086         this.mainHead.remove();
7087         this.mainHead = table.createChild(header, this.mainBody, false);
7088     },
7089     
7090     onHiddenChange : function(colModel, colIndex, hidden)
7091     {
7092         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7093         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7094         
7095         this.CSS.updateRule(thSelector, "display", "");
7096         this.CSS.updateRule(tdSelector, "display", "");
7097         
7098         if(hidden){
7099             this.CSS.updateRule(thSelector, "display", "none");
7100             this.CSS.updateRule(tdSelector, "display", "none");
7101         }
7102         
7103         this.onHeaderChange();
7104         this.onLoad();
7105     },
7106     
7107     setColumnWidth: function(col_index, width)
7108     {
7109         // width = "md-2 xs-2..."
7110         if(!this.colModel.config[col_index]) {
7111             return;
7112         }
7113         
7114         var w = width.split(" ");
7115         
7116         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7117         
7118         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7119         
7120         
7121         for(var j = 0; j < w.length; j++) {
7122             
7123             if(!w[j]) {
7124                 continue;
7125             }
7126             
7127             var size_cls = w[j].split("-");
7128             
7129             if(!Number.isInteger(size_cls[1] * 1)) {
7130                 continue;
7131             }
7132             
7133             if(!this.colModel.config[col_index][size_cls[0]]) {
7134                 continue;
7135             }
7136             
7137             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7138                 continue;
7139             }
7140             
7141             h_row[0].classList.replace(
7142                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7143                 "col-"+size_cls[0]+"-"+size_cls[1]
7144             );
7145             
7146             for(var i = 0; i < rows.length; i++) {
7147                 
7148                 var size_cls = w[j].split("-");
7149                 
7150                 if(!Number.isInteger(size_cls[1] * 1)) {
7151                     continue;
7152                 }
7153                 
7154                 if(!this.colModel.config[col_index][size_cls[0]]) {
7155                     continue;
7156                 }
7157                 
7158                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7159                     continue;
7160                 }
7161                 
7162                 rows[i].classList.replace(
7163                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7164                     "col-"+size_cls[0]+"-"+size_cls[1]
7165                 );
7166             }
7167             
7168             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7169         }
7170     }
7171 });
7172
7173  
7174
7175  /*
7176  * - LGPL
7177  *
7178  * table cell
7179  * 
7180  */
7181
7182 /**
7183  * @class Roo.bootstrap.TableCell
7184  * @extends Roo.bootstrap.Component
7185  * Bootstrap TableCell class
7186  * @cfg {String} html cell contain text
7187  * @cfg {String} cls cell class
7188  * @cfg {String} tag cell tag (td|th) default td
7189  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7190  * @cfg {String} align Aligns the content in a cell
7191  * @cfg {String} axis Categorizes cells
7192  * @cfg {String} bgcolor Specifies the background color of a cell
7193  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7194  * @cfg {Number} colspan Specifies the number of columns a cell should span
7195  * @cfg {String} headers Specifies one or more header cells a cell is related to
7196  * @cfg {Number} height Sets the height of a cell
7197  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7198  * @cfg {Number} rowspan Sets the number of rows a cell should span
7199  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7200  * @cfg {String} valign Vertical aligns the content in a cell
7201  * @cfg {Number} width Specifies the width of a cell
7202  * 
7203  * @constructor
7204  * Create a new TableCell
7205  * @param {Object} config The config object
7206  */
7207
7208 Roo.bootstrap.TableCell = function(config){
7209     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7210 };
7211
7212 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7213     
7214     html: false,
7215     cls: false,
7216     tag: false,
7217     abbr: false,
7218     align: false,
7219     axis: false,
7220     bgcolor: false,
7221     charoff: false,
7222     colspan: false,
7223     headers: false,
7224     height: false,
7225     nowrap: false,
7226     rowspan: false,
7227     scope: false,
7228     valign: false,
7229     width: false,
7230     
7231     
7232     getAutoCreate : function(){
7233         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7234         
7235         cfg = {
7236             tag: 'td'
7237         };
7238         
7239         if(this.tag){
7240             cfg.tag = this.tag;
7241         }
7242         
7243         if (this.html) {
7244             cfg.html=this.html
7245         }
7246         if (this.cls) {
7247             cfg.cls=this.cls
7248         }
7249         if (this.abbr) {
7250             cfg.abbr=this.abbr
7251         }
7252         if (this.align) {
7253             cfg.align=this.align
7254         }
7255         if (this.axis) {
7256             cfg.axis=this.axis
7257         }
7258         if (this.bgcolor) {
7259             cfg.bgcolor=this.bgcolor
7260         }
7261         if (this.charoff) {
7262             cfg.charoff=this.charoff
7263         }
7264         if (this.colspan) {
7265             cfg.colspan=this.colspan
7266         }
7267         if (this.headers) {
7268             cfg.headers=this.headers
7269         }
7270         if (this.height) {
7271             cfg.height=this.height
7272         }
7273         if (this.nowrap) {
7274             cfg.nowrap=this.nowrap
7275         }
7276         if (this.rowspan) {
7277             cfg.rowspan=this.rowspan
7278         }
7279         if (this.scope) {
7280             cfg.scope=this.scope
7281         }
7282         if (this.valign) {
7283             cfg.valign=this.valign
7284         }
7285         if (this.width) {
7286             cfg.width=this.width
7287         }
7288         
7289         
7290         return cfg;
7291     }
7292    
7293 });
7294
7295  
7296
7297  /*
7298  * - LGPL
7299  *
7300  * table row
7301  * 
7302  */
7303
7304 /**
7305  * @class Roo.bootstrap.TableRow
7306  * @extends Roo.bootstrap.Component
7307  * Bootstrap TableRow class
7308  * @cfg {String} cls row class
7309  * @cfg {String} align Aligns the content in a table row
7310  * @cfg {String} bgcolor Specifies a background color for a table row
7311  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7312  * @cfg {String} valign Vertical aligns the content in a table row
7313  * 
7314  * @constructor
7315  * Create a new TableRow
7316  * @param {Object} config The config object
7317  */
7318
7319 Roo.bootstrap.TableRow = function(config){
7320     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7321 };
7322
7323 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7324     
7325     cls: false,
7326     align: false,
7327     bgcolor: false,
7328     charoff: false,
7329     valign: false,
7330     
7331     getAutoCreate : function(){
7332         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7333         
7334         cfg = {
7335             tag: 'tr'
7336         };
7337             
7338         if(this.cls){
7339             cfg.cls = this.cls;
7340         }
7341         if(this.align){
7342             cfg.align = this.align;
7343         }
7344         if(this.bgcolor){
7345             cfg.bgcolor = this.bgcolor;
7346         }
7347         if(this.charoff){
7348             cfg.charoff = this.charoff;
7349         }
7350         if(this.valign){
7351             cfg.valign = this.valign;
7352         }
7353         
7354         return cfg;
7355     }
7356    
7357 });
7358
7359  
7360
7361  /*
7362  * - LGPL
7363  *
7364  * table body
7365  * 
7366  */
7367
7368 /**
7369  * @class Roo.bootstrap.TableBody
7370  * @extends Roo.bootstrap.Component
7371  * Bootstrap TableBody class
7372  * @cfg {String} cls element class
7373  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7374  * @cfg {String} align Aligns the content inside the element
7375  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7376  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7377  * 
7378  * @constructor
7379  * Create a new TableBody
7380  * @param {Object} config The config object
7381  */
7382
7383 Roo.bootstrap.TableBody = function(config){
7384     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7385 };
7386
7387 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7388     
7389     cls: false,
7390     tag: false,
7391     align: false,
7392     charoff: false,
7393     valign: false,
7394     
7395     getAutoCreate : function(){
7396         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7397         
7398         cfg = {
7399             tag: 'tbody'
7400         };
7401             
7402         if (this.cls) {
7403             cfg.cls=this.cls
7404         }
7405         if(this.tag){
7406             cfg.tag = this.tag;
7407         }
7408         
7409         if(this.align){
7410             cfg.align = this.align;
7411         }
7412         if(this.charoff){
7413             cfg.charoff = this.charoff;
7414         }
7415         if(this.valign){
7416             cfg.valign = this.valign;
7417         }
7418         
7419         return cfg;
7420     }
7421     
7422     
7423 //    initEvents : function()
7424 //    {
7425 //        
7426 //        if(!this.store){
7427 //            return;
7428 //        }
7429 //        
7430 //        this.store = Roo.factory(this.store, Roo.data);
7431 //        this.store.on('load', this.onLoad, this);
7432 //        
7433 //        this.store.load();
7434 //        
7435 //    },
7436 //    
7437 //    onLoad: function () 
7438 //    {   
7439 //        this.fireEvent('load', this);
7440 //    }
7441 //    
7442 //   
7443 });
7444
7445  
7446
7447  /*
7448  * Based on:
7449  * Ext JS Library 1.1.1
7450  * Copyright(c) 2006-2007, Ext JS, LLC.
7451  *
7452  * Originally Released Under LGPL - original licence link has changed is not relivant.
7453  *
7454  * Fork - LGPL
7455  * <script type="text/javascript">
7456  */
7457
7458 // as we use this in bootstrap.
7459 Roo.namespace('Roo.form');
7460  /**
7461  * @class Roo.form.Action
7462  * Internal Class used to handle form actions
7463  * @constructor
7464  * @param {Roo.form.BasicForm} el The form element or its id
7465  * @param {Object} config Configuration options
7466  */
7467
7468  
7469  
7470 // define the action interface
7471 Roo.form.Action = function(form, options){
7472     this.form = form;
7473     this.options = options || {};
7474 };
7475 /**
7476  * Client Validation Failed
7477  * @const 
7478  */
7479 Roo.form.Action.CLIENT_INVALID = 'client';
7480 /**
7481  * Server Validation Failed
7482  * @const 
7483  */
7484 Roo.form.Action.SERVER_INVALID = 'server';
7485  /**
7486  * Connect to Server Failed
7487  * @const 
7488  */
7489 Roo.form.Action.CONNECT_FAILURE = 'connect';
7490 /**
7491  * Reading Data from Server Failed
7492  * @const 
7493  */
7494 Roo.form.Action.LOAD_FAILURE = 'load';
7495
7496 Roo.form.Action.prototype = {
7497     type : 'default',
7498     failureType : undefined,
7499     response : undefined,
7500     result : undefined,
7501
7502     // interface method
7503     run : function(options){
7504
7505     },
7506
7507     // interface method
7508     success : function(response){
7509
7510     },
7511
7512     // interface method
7513     handleResponse : function(response){
7514
7515     },
7516
7517     // default connection failure
7518     failure : function(response){
7519         
7520         this.response = response;
7521         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7522         this.form.afterAction(this, false);
7523     },
7524
7525     processResponse : function(response){
7526         this.response = response;
7527         if(!response.responseText){
7528             return true;
7529         }
7530         this.result = this.handleResponse(response);
7531         return this.result;
7532     },
7533
7534     // utility functions used internally
7535     getUrl : function(appendParams){
7536         var url = this.options.url || this.form.url || this.form.el.dom.action;
7537         if(appendParams){
7538             var p = this.getParams();
7539             if(p){
7540                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7541             }
7542         }
7543         return url;
7544     },
7545
7546     getMethod : function(){
7547         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7548     },
7549
7550     getParams : function(){
7551         var bp = this.form.baseParams;
7552         var p = this.options.params;
7553         if(p){
7554             if(typeof p == "object"){
7555                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7556             }else if(typeof p == 'string' && bp){
7557                 p += '&' + Roo.urlEncode(bp);
7558             }
7559         }else if(bp){
7560             p = Roo.urlEncode(bp);
7561         }
7562         return p;
7563     },
7564
7565     createCallback : function(){
7566         return {
7567             success: this.success,
7568             failure: this.failure,
7569             scope: this,
7570             timeout: (this.form.timeout*1000),
7571             upload: this.form.fileUpload ? this.success : undefined
7572         };
7573     }
7574 };
7575
7576 Roo.form.Action.Submit = function(form, options){
7577     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7578 };
7579
7580 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7581     type : 'submit',
7582
7583     haveProgress : false,
7584     uploadComplete : false,
7585     
7586     // uploadProgress indicator.
7587     uploadProgress : function()
7588     {
7589         if (!this.form.progressUrl) {
7590             return;
7591         }
7592         
7593         if (!this.haveProgress) {
7594             Roo.MessageBox.progress("Uploading", "Uploading");
7595         }
7596         if (this.uploadComplete) {
7597            Roo.MessageBox.hide();
7598            return;
7599         }
7600         
7601         this.haveProgress = true;
7602    
7603         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7604         
7605         var c = new Roo.data.Connection();
7606         c.request({
7607             url : this.form.progressUrl,
7608             params: {
7609                 id : uid
7610             },
7611             method: 'GET',
7612             success : function(req){
7613                //console.log(data);
7614                 var rdata = false;
7615                 var edata;
7616                 try  {
7617                    rdata = Roo.decode(req.responseText)
7618                 } catch (e) {
7619                     Roo.log("Invalid data from server..");
7620                     Roo.log(edata);
7621                     return;
7622                 }
7623                 if (!rdata || !rdata.success) {
7624                     Roo.log(rdata);
7625                     Roo.MessageBox.alert(Roo.encode(rdata));
7626                     return;
7627                 }
7628                 var data = rdata.data;
7629                 
7630                 if (this.uploadComplete) {
7631                    Roo.MessageBox.hide();
7632                    return;
7633                 }
7634                    
7635                 if (data){
7636                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7637                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7638                     );
7639                 }
7640                 this.uploadProgress.defer(2000,this);
7641             },
7642        
7643             failure: function(data) {
7644                 Roo.log('progress url failed ');
7645                 Roo.log(data);
7646             },
7647             scope : this
7648         });
7649            
7650     },
7651     
7652     
7653     run : function()
7654     {
7655         // run get Values on the form, so it syncs any secondary forms.
7656         this.form.getValues();
7657         
7658         var o = this.options;
7659         var method = this.getMethod();
7660         var isPost = method == 'POST';
7661         if(o.clientValidation === false || this.form.isValid()){
7662             
7663             if (this.form.progressUrl) {
7664                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7665                     (new Date() * 1) + '' + Math.random());
7666                     
7667             } 
7668             
7669             
7670             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7671                 form:this.form.el.dom,
7672                 url:this.getUrl(!isPost),
7673                 method: method,
7674                 params:isPost ? this.getParams() : null,
7675                 isUpload: this.form.fileUpload
7676             }));
7677             
7678             this.uploadProgress();
7679
7680         }else if (o.clientValidation !== false){ // client validation failed
7681             this.failureType = Roo.form.Action.CLIENT_INVALID;
7682             this.form.afterAction(this, false);
7683         }
7684     },
7685
7686     success : function(response)
7687     {
7688         this.uploadComplete= true;
7689         if (this.haveProgress) {
7690             Roo.MessageBox.hide();
7691         }
7692         
7693         
7694         var result = this.processResponse(response);
7695         if(result === true || result.success){
7696             this.form.afterAction(this, true);
7697             return;
7698         }
7699         if(result.errors){
7700             this.form.markInvalid(result.errors);
7701             this.failureType = Roo.form.Action.SERVER_INVALID;
7702         }
7703         this.form.afterAction(this, false);
7704     },
7705     failure : function(response)
7706     {
7707         this.uploadComplete= true;
7708         if (this.haveProgress) {
7709             Roo.MessageBox.hide();
7710         }
7711         
7712         this.response = response;
7713         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7714         this.form.afterAction(this, false);
7715     },
7716     
7717     handleResponse : function(response){
7718         if(this.form.errorReader){
7719             var rs = this.form.errorReader.read(response);
7720             var errors = [];
7721             if(rs.records){
7722                 for(var i = 0, len = rs.records.length; i < len; i++) {
7723                     var r = rs.records[i];
7724                     errors[i] = r.data;
7725                 }
7726             }
7727             if(errors.length < 1){
7728                 errors = null;
7729             }
7730             return {
7731                 success : rs.success,
7732                 errors : errors
7733             };
7734         }
7735         var ret = false;
7736         try {
7737             ret = Roo.decode(response.responseText);
7738         } catch (e) {
7739             ret = {
7740                 success: false,
7741                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7742                 errors : []
7743             };
7744         }
7745         return ret;
7746         
7747     }
7748 });
7749
7750
7751 Roo.form.Action.Load = function(form, options){
7752     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7753     this.reader = this.form.reader;
7754 };
7755
7756 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7757     type : 'load',
7758
7759     run : function(){
7760         
7761         Roo.Ajax.request(Roo.apply(
7762                 this.createCallback(), {
7763                     method:this.getMethod(),
7764                     url:this.getUrl(false),
7765                     params:this.getParams()
7766         }));
7767     },
7768
7769     success : function(response){
7770         
7771         var result = this.processResponse(response);
7772         if(result === true || !result.success || !result.data){
7773             this.failureType = Roo.form.Action.LOAD_FAILURE;
7774             this.form.afterAction(this, false);
7775             return;
7776         }
7777         this.form.clearInvalid();
7778         this.form.setValues(result.data);
7779         this.form.afterAction(this, true);
7780     },
7781
7782     handleResponse : function(response){
7783         if(this.form.reader){
7784             var rs = this.form.reader.read(response);
7785             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7786             return {
7787                 success : rs.success,
7788                 data : data
7789             };
7790         }
7791         return Roo.decode(response.responseText);
7792     }
7793 });
7794
7795 Roo.form.Action.ACTION_TYPES = {
7796     'load' : Roo.form.Action.Load,
7797     'submit' : Roo.form.Action.Submit
7798 };/*
7799  * - LGPL
7800  *
7801  * form
7802  *
7803  */
7804
7805 /**
7806  * @class Roo.bootstrap.Form
7807  * @extends Roo.bootstrap.Component
7808  * Bootstrap Form class
7809  * @cfg {String} method  GET | POST (default POST)
7810  * @cfg {String} labelAlign top | left (default top)
7811  * @cfg {String} align left  | right - for navbars
7812  * @cfg {Boolean} loadMask load mask when submit (default true)
7813
7814  *
7815  * @constructor
7816  * Create a new Form
7817  * @param {Object} config The config object
7818  */
7819
7820
7821 Roo.bootstrap.Form = function(config){
7822     
7823     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7824     
7825     Roo.bootstrap.Form.popover.apply();
7826     
7827     this.addEvents({
7828         /**
7829          * @event clientvalidation
7830          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7831          * @param {Form} this
7832          * @param {Boolean} valid true if the form has passed client-side validation
7833          */
7834         clientvalidation: true,
7835         /**
7836          * @event beforeaction
7837          * Fires before any action is performed. Return false to cancel the action.
7838          * @param {Form} this
7839          * @param {Action} action The action to be performed
7840          */
7841         beforeaction: true,
7842         /**
7843          * @event actionfailed
7844          * Fires when an action fails.
7845          * @param {Form} this
7846          * @param {Action} action The action that failed
7847          */
7848         actionfailed : true,
7849         /**
7850          * @event actioncomplete
7851          * Fires when an action is completed.
7852          * @param {Form} this
7853          * @param {Action} action The action that completed
7854          */
7855         actioncomplete : true
7856     });
7857 };
7858
7859 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7860
7861      /**
7862      * @cfg {String} method
7863      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7864      */
7865     method : 'POST',
7866     /**
7867      * @cfg {String} url
7868      * The URL to use for form actions if one isn't supplied in the action options.
7869      */
7870     /**
7871      * @cfg {Boolean} fileUpload
7872      * Set to true if this form is a file upload.
7873      */
7874
7875     /**
7876      * @cfg {Object} baseParams
7877      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7878      */
7879
7880     /**
7881      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7882      */
7883     timeout: 30,
7884     /**
7885      * @cfg {Sting} align (left|right) for navbar forms
7886      */
7887     align : 'left',
7888
7889     // private
7890     activeAction : null,
7891
7892     /**
7893      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7894      * element by passing it or its id or mask the form itself by passing in true.
7895      * @type Mixed
7896      */
7897     waitMsgTarget : false,
7898
7899     loadMask : true,
7900     
7901     /**
7902      * @cfg {Boolean} errorMask (true|false) default false
7903      */
7904     errorMask : false,
7905     
7906     /**
7907      * @cfg {Number} maskOffset Default 100
7908      */
7909     maskOffset : 100,
7910     
7911     /**
7912      * @cfg {Boolean} maskBody
7913      */
7914     maskBody : false,
7915
7916     getAutoCreate : function(){
7917
7918         var cfg = {
7919             tag: 'form',
7920             method : this.method || 'POST',
7921             id : this.id || Roo.id(),
7922             cls : ''
7923         };
7924         if (this.parent().xtype.match(/^Nav/)) {
7925             cfg.cls = 'navbar-form navbar-' + this.align;
7926
7927         }
7928
7929         if (this.labelAlign == 'left' ) {
7930             cfg.cls += ' form-horizontal';
7931         }
7932
7933
7934         return cfg;
7935     },
7936     initEvents : function()
7937     {
7938         this.el.on('submit', this.onSubmit, this);
7939         // this was added as random key presses on the form where triggering form submit.
7940         this.el.on('keypress', function(e) {
7941             if (e.getCharCode() != 13) {
7942                 return true;
7943             }
7944             // we might need to allow it for textareas.. and some other items.
7945             // check e.getTarget().
7946
7947             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7948                 return true;
7949             }
7950
7951             Roo.log("keypress blocked");
7952
7953             e.preventDefault();
7954             return false;
7955         });
7956         
7957     },
7958     // private
7959     onSubmit : function(e){
7960         e.stopEvent();
7961     },
7962
7963      /**
7964      * Returns true if client-side validation on the form is successful.
7965      * @return Boolean
7966      */
7967     isValid : function(){
7968         var items = this.getItems();
7969         var valid = true;
7970         var target = false;
7971         
7972         items.each(function(f){
7973             
7974             if(f.validate()){
7975                 return;
7976             }
7977             
7978             Roo.log('invalid field: ' + f.name);
7979             
7980             valid = false;
7981
7982             if(!target && f.el.isVisible(true)){
7983                 target = f;
7984             }
7985            
7986         });
7987         
7988         if(this.errorMask && !valid){
7989             Roo.bootstrap.Form.popover.mask(this, target);
7990         }
7991         
7992         return valid;
7993     },
7994     
7995     /**
7996      * Returns true if any fields in this form have changed since their original load.
7997      * @return Boolean
7998      */
7999     isDirty : function(){
8000         var dirty = false;
8001         var items = this.getItems();
8002         items.each(function(f){
8003            if(f.isDirty()){
8004                dirty = true;
8005                return false;
8006            }
8007            return true;
8008         });
8009         return dirty;
8010     },
8011      /**
8012      * Performs a predefined action (submit or load) or custom actions you define on this form.
8013      * @param {String} actionName The name of the action type
8014      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8015      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8016      * accept other config options):
8017      * <pre>
8018 Property          Type             Description
8019 ----------------  ---------------  ----------------------------------------------------------------------------------
8020 url               String           The url for the action (defaults to the form's url)
8021 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8022 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8023 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8024                                    validate the form on the client (defaults to false)
8025      * </pre>
8026      * @return {BasicForm} this
8027      */
8028     doAction : function(action, options){
8029         if(typeof action == 'string'){
8030             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8031         }
8032         if(this.fireEvent('beforeaction', this, action) !== false){
8033             this.beforeAction(action);
8034             action.run.defer(100, action);
8035         }
8036         return this;
8037     },
8038
8039     // private
8040     beforeAction : function(action){
8041         var o = action.options;
8042         
8043         if(this.loadMask){
8044             
8045             if(this.maskBody){
8046                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8047             } else {
8048                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8049             }
8050         }
8051         // not really supported yet.. ??
8052
8053         //if(this.waitMsgTarget === true){
8054         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8055         //}else if(this.waitMsgTarget){
8056         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8057         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8058         //}else {
8059         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8060        // }
8061
8062     },
8063
8064     // private
8065     afterAction : function(action, success){
8066         this.activeAction = null;
8067         var o = action.options;
8068
8069         if(this.loadMask){
8070             
8071             if(this.maskBody){
8072                 Roo.get(document.body).unmask();
8073             } else {
8074                 this.el.unmask();
8075             }
8076         }
8077         
8078         //if(this.waitMsgTarget === true){
8079 //            this.el.unmask();
8080         //}else if(this.waitMsgTarget){
8081         //    this.waitMsgTarget.unmask();
8082         //}else{
8083         //    Roo.MessageBox.updateProgress(1);
8084         //    Roo.MessageBox.hide();
8085        // }
8086         //
8087         if(success){
8088             if(o.reset){
8089                 this.reset();
8090             }
8091             Roo.callback(o.success, o.scope, [this, action]);
8092             this.fireEvent('actioncomplete', this, action);
8093
8094         }else{
8095
8096             // failure condition..
8097             // we have a scenario where updates need confirming.
8098             // eg. if a locking scenario exists..
8099             // we look for { errors : { needs_confirm : true }} in the response.
8100             if (
8101                 (typeof(action.result) != 'undefined')  &&
8102                 (typeof(action.result.errors) != 'undefined')  &&
8103                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8104            ){
8105                 var _t = this;
8106                 Roo.log("not supported yet");
8107                  /*
8108
8109                 Roo.MessageBox.confirm(
8110                     "Change requires confirmation",
8111                     action.result.errorMsg,
8112                     function(r) {
8113                         if (r != 'yes') {
8114                             return;
8115                         }
8116                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8117                     }
8118
8119                 );
8120                 */
8121
8122
8123                 return;
8124             }
8125
8126             Roo.callback(o.failure, o.scope, [this, action]);
8127             // show an error message if no failed handler is set..
8128             if (!this.hasListener('actionfailed')) {
8129                 Roo.log("need to add dialog support");
8130                 /*
8131                 Roo.MessageBox.alert("Error",
8132                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8133                         action.result.errorMsg :
8134                         "Saving Failed, please check your entries or try again"
8135                 );
8136                 */
8137             }
8138
8139             this.fireEvent('actionfailed', this, action);
8140         }
8141
8142     },
8143     /**
8144      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8145      * @param {String} id The value to search for
8146      * @return Field
8147      */
8148     findField : function(id){
8149         var items = this.getItems();
8150         var field = items.get(id);
8151         if(!field){
8152              items.each(function(f){
8153                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8154                     field = f;
8155                     return false;
8156                 }
8157                 return true;
8158             });
8159         }
8160         return field || null;
8161     },
8162      /**
8163      * Mark fields in this form invalid in bulk.
8164      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8165      * @return {BasicForm} this
8166      */
8167     markInvalid : function(errors){
8168         if(errors instanceof Array){
8169             for(var i = 0, len = errors.length; i < len; i++){
8170                 var fieldError = errors[i];
8171                 var f = this.findField(fieldError.id);
8172                 if(f){
8173                     f.markInvalid(fieldError.msg);
8174                 }
8175             }
8176         }else{
8177             var field, id;
8178             for(id in errors){
8179                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8180                     field.markInvalid(errors[id]);
8181                 }
8182             }
8183         }
8184         //Roo.each(this.childForms || [], function (f) {
8185         //    f.markInvalid(errors);
8186         //});
8187
8188         return this;
8189     },
8190
8191     /**
8192      * Set values for fields in this form in bulk.
8193      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8194      * @return {BasicForm} this
8195      */
8196     setValues : function(values){
8197         if(values instanceof Array){ // array of objects
8198             for(var i = 0, len = values.length; i < len; i++){
8199                 var v = values[i];
8200                 var f = this.findField(v.id);
8201                 if(f){
8202                     f.setValue(v.value);
8203                     if(this.trackResetOnLoad){
8204                         f.originalValue = f.getValue();
8205                     }
8206                 }
8207             }
8208         }else{ // object hash
8209             var field, id;
8210             for(id in values){
8211                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8212
8213                     if (field.setFromData &&
8214                         field.valueField &&
8215                         field.displayField &&
8216                         // combos' with local stores can
8217                         // be queried via setValue()
8218                         // to set their value..
8219                         (field.store && !field.store.isLocal)
8220                         ) {
8221                         // it's a combo
8222                         var sd = { };
8223                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8224                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8225                         field.setFromData(sd);
8226
8227                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8228                         
8229                         field.setFromData(values);
8230                         
8231                     } else {
8232                         field.setValue(values[id]);
8233                     }
8234
8235
8236                     if(this.trackResetOnLoad){
8237                         field.originalValue = field.getValue();
8238                     }
8239                 }
8240             }
8241         }
8242
8243         //Roo.each(this.childForms || [], function (f) {
8244         //    f.setValues(values);
8245         //});
8246
8247         return this;
8248     },
8249
8250     /**
8251      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8252      * they are returned as an array.
8253      * @param {Boolean} asString
8254      * @return {Object}
8255      */
8256     getValues : function(asString){
8257         //if (this.childForms) {
8258             // copy values from the child forms
8259         //    Roo.each(this.childForms, function (f) {
8260         //        this.setValues(f.getValues());
8261         //    }, this);
8262         //}
8263
8264
8265
8266         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8267         if(asString === true){
8268             return fs;
8269         }
8270         return Roo.urlDecode(fs);
8271     },
8272
8273     /**
8274      * Returns the fields in this form as an object with key/value pairs.
8275      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8276      * @return {Object}
8277      */
8278     getFieldValues : function(with_hidden)
8279     {
8280         var items = this.getItems();
8281         var ret = {};
8282         items.each(function(f){
8283             
8284             if (!f.getName()) {
8285                 return;
8286             }
8287             
8288             var v = f.getValue();
8289             
8290             if (f.inputType =='radio') {
8291                 if (typeof(ret[f.getName()]) == 'undefined') {
8292                     ret[f.getName()] = ''; // empty..
8293                 }
8294
8295                 if (!f.el.dom.checked) {
8296                     return;
8297
8298                 }
8299                 v = f.el.dom.value;
8300
8301             }
8302             
8303             if(f.xtype == 'MoneyField'){
8304                 ret[f.currencyName] = f.getCurrency();
8305             }
8306
8307             // not sure if this supported any more..
8308             if ((typeof(v) == 'object') && f.getRawValue) {
8309                 v = f.getRawValue() ; // dates..
8310             }
8311             // combo boxes where name != hiddenName...
8312             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8313                 ret[f.name] = f.getRawValue();
8314             }
8315             ret[f.getName()] = v;
8316         });
8317
8318         return ret;
8319     },
8320
8321     /**
8322      * Clears all invalid messages in this form.
8323      * @return {BasicForm} this
8324      */
8325     clearInvalid : function(){
8326         var items = this.getItems();
8327
8328         items.each(function(f){
8329            f.clearInvalid();
8330         });
8331
8332         return this;
8333     },
8334
8335     /**
8336      * Resets this form.
8337      * @return {BasicForm} this
8338      */
8339     reset : function(){
8340         var items = this.getItems();
8341         items.each(function(f){
8342             f.reset();
8343         });
8344
8345         Roo.each(this.childForms || [], function (f) {
8346             f.reset();
8347         });
8348
8349
8350         return this;
8351     },
8352     
8353     getItems : function()
8354     {
8355         var r=new Roo.util.MixedCollection(false, function(o){
8356             return o.id || (o.id = Roo.id());
8357         });
8358         var iter = function(el) {
8359             if (el.inputEl) {
8360                 r.add(el);
8361             }
8362             if (!el.items) {
8363                 return;
8364             }
8365             Roo.each(el.items,function(e) {
8366                 iter(e);
8367             });
8368         };
8369
8370         iter(this);
8371         return r;
8372     },
8373     
8374     hideFields : function(items)
8375     {
8376         Roo.each(items, function(i){
8377             
8378             var f = this.findField(i);
8379             
8380             if(!f){
8381                 return;
8382             }
8383             
8384             f.hide();
8385             
8386         }, this);
8387     },
8388     
8389     showFields : function(items)
8390     {
8391         Roo.each(items, function(i){
8392             
8393             var f = this.findField(i);
8394             
8395             if(!f){
8396                 return;
8397             }
8398             
8399             f.show();
8400             
8401         }, this);
8402     }
8403
8404 });
8405
8406 Roo.apply(Roo.bootstrap.Form, {
8407     
8408     popover : {
8409         
8410         padding : 5,
8411         
8412         isApplied : false,
8413         
8414         isMasked : false,
8415         
8416         form : false,
8417         
8418         target : false,
8419         
8420         toolTip : false,
8421         
8422         intervalID : false,
8423         
8424         maskEl : false,
8425         
8426         apply : function()
8427         {
8428             if(this.isApplied){
8429                 return;
8430             }
8431             
8432             this.maskEl = {
8433                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8434                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8435                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8436                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8437             };
8438             
8439             this.maskEl.top.enableDisplayMode("block");
8440             this.maskEl.left.enableDisplayMode("block");
8441             this.maskEl.bottom.enableDisplayMode("block");
8442             this.maskEl.right.enableDisplayMode("block");
8443             
8444             this.toolTip = new Roo.bootstrap.Tooltip({
8445                 cls : 'roo-form-error-popover',
8446                 alignment : {
8447                     'left' : ['r-l', [-2,0], 'right'],
8448                     'right' : ['l-r', [2,0], 'left'],
8449                     'bottom' : ['tl-bl', [0,2], 'top'],
8450                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8451                 }
8452             });
8453             
8454             this.toolTip.render(Roo.get(document.body));
8455
8456             this.toolTip.el.enableDisplayMode("block");
8457             
8458             Roo.get(document.body).on('click', function(){
8459                 this.unmask();
8460             }, this);
8461             
8462             Roo.get(document.body).on('touchstart', function(){
8463                 this.unmask();
8464             }, this);
8465             
8466             this.isApplied = true
8467         },
8468         
8469         mask : function(form, target)
8470         {
8471             this.form = form;
8472             
8473             this.target = target;
8474             
8475             if(!this.form.errorMask || !target.el){
8476                 return;
8477             }
8478             
8479             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8480             
8481             Roo.log(scrollable);
8482             
8483             var ot = this.target.el.calcOffsetsTo(scrollable);
8484             
8485             var scrollTo = ot[1] - this.form.maskOffset;
8486             
8487             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8488             
8489             scrollable.scrollTo('top', scrollTo);
8490             
8491             var box = this.target.el.getBox();
8492             Roo.log(box);
8493             var zIndex = Roo.bootstrap.Modal.zIndex++;
8494
8495             
8496             this.maskEl.top.setStyle('position', 'absolute');
8497             this.maskEl.top.setStyle('z-index', zIndex);
8498             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8499             this.maskEl.top.setLeft(0);
8500             this.maskEl.top.setTop(0);
8501             this.maskEl.top.show();
8502             
8503             this.maskEl.left.setStyle('position', 'absolute');
8504             this.maskEl.left.setStyle('z-index', zIndex);
8505             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8506             this.maskEl.left.setLeft(0);
8507             this.maskEl.left.setTop(box.y - this.padding);
8508             this.maskEl.left.show();
8509
8510             this.maskEl.bottom.setStyle('position', 'absolute');
8511             this.maskEl.bottom.setStyle('z-index', zIndex);
8512             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8513             this.maskEl.bottom.setLeft(0);
8514             this.maskEl.bottom.setTop(box.bottom + this.padding);
8515             this.maskEl.bottom.show();
8516
8517             this.maskEl.right.setStyle('position', 'absolute');
8518             this.maskEl.right.setStyle('z-index', zIndex);
8519             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8520             this.maskEl.right.setLeft(box.right + this.padding);
8521             this.maskEl.right.setTop(box.y - this.padding);
8522             this.maskEl.right.show();
8523
8524             this.toolTip.bindEl = this.target.el;
8525
8526             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8527
8528             var tip = this.target.blankText;
8529
8530             if(this.target.getValue() !== '' ) {
8531                 
8532                 if (this.target.invalidText.length) {
8533                     tip = this.target.invalidText;
8534                 } else if (this.target.regexText.length){
8535                     tip = this.target.regexText;
8536                 }
8537             }
8538
8539             this.toolTip.show(tip);
8540
8541             this.intervalID = window.setInterval(function() {
8542                 Roo.bootstrap.Form.popover.unmask();
8543             }, 10000);
8544
8545             window.onwheel = function(){ return false;};
8546             
8547             (function(){ this.isMasked = true; }).defer(500, this);
8548             
8549         },
8550         
8551         unmask : function()
8552         {
8553             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8554                 return;
8555             }
8556             
8557             this.maskEl.top.setStyle('position', 'absolute');
8558             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8559             this.maskEl.top.hide();
8560
8561             this.maskEl.left.setStyle('position', 'absolute');
8562             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8563             this.maskEl.left.hide();
8564
8565             this.maskEl.bottom.setStyle('position', 'absolute');
8566             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8567             this.maskEl.bottom.hide();
8568
8569             this.maskEl.right.setStyle('position', 'absolute');
8570             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8571             this.maskEl.right.hide();
8572             
8573             this.toolTip.hide();
8574             
8575             this.toolTip.el.hide();
8576             
8577             window.onwheel = function(){ return true;};
8578             
8579             if(this.intervalID){
8580                 window.clearInterval(this.intervalID);
8581                 this.intervalID = false;
8582             }
8583             
8584             this.isMasked = false;
8585             
8586         }
8587         
8588     }
8589     
8590 });
8591
8592 /*
8593  * Based on:
8594  * Ext JS Library 1.1.1
8595  * Copyright(c) 2006-2007, Ext JS, LLC.
8596  *
8597  * Originally Released Under LGPL - original licence link has changed is not relivant.
8598  *
8599  * Fork - LGPL
8600  * <script type="text/javascript">
8601  */
8602 /**
8603  * @class Roo.form.VTypes
8604  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8605  * @singleton
8606  */
8607 Roo.form.VTypes = function(){
8608     // closure these in so they are only created once.
8609     var alpha = /^[a-zA-Z_]+$/;
8610     var alphanum = /^[a-zA-Z0-9_]+$/;
8611     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8612     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8613
8614     // All these messages and functions are configurable
8615     return {
8616         /**
8617          * The function used to validate email addresses
8618          * @param {String} value The email address
8619          */
8620         'email' : function(v){
8621             return email.test(v);
8622         },
8623         /**
8624          * The error text to display when the email validation function returns false
8625          * @type String
8626          */
8627         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8628         /**
8629          * The keystroke filter mask to be applied on email input
8630          * @type RegExp
8631          */
8632         'emailMask' : /[a-z0-9_\.\-@]/i,
8633
8634         /**
8635          * The function used to validate URLs
8636          * @param {String} value The URL
8637          */
8638         'url' : function(v){
8639             return url.test(v);
8640         },
8641         /**
8642          * The error text to display when the url validation function returns false
8643          * @type String
8644          */
8645         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8646         
8647         /**
8648          * The function used to validate alpha values
8649          * @param {String} value The value
8650          */
8651         'alpha' : function(v){
8652             return alpha.test(v);
8653         },
8654         /**
8655          * The error text to display when the alpha validation function returns false
8656          * @type String
8657          */
8658         'alphaText' : 'This field should only contain letters and _',
8659         /**
8660          * The keystroke filter mask to be applied on alpha input
8661          * @type RegExp
8662          */
8663         'alphaMask' : /[a-z_]/i,
8664
8665         /**
8666          * The function used to validate alphanumeric values
8667          * @param {String} value The value
8668          */
8669         'alphanum' : function(v){
8670             return alphanum.test(v);
8671         },
8672         /**
8673          * The error text to display when the alphanumeric validation function returns false
8674          * @type String
8675          */
8676         'alphanumText' : 'This field should only contain letters, numbers and _',
8677         /**
8678          * The keystroke filter mask to be applied on alphanumeric input
8679          * @type RegExp
8680          */
8681         'alphanumMask' : /[a-z0-9_]/i
8682     };
8683 }();/*
8684  * - LGPL
8685  *
8686  * Input
8687  * 
8688  */
8689
8690 /**
8691  * @class Roo.bootstrap.Input
8692  * @extends Roo.bootstrap.Component
8693  * Bootstrap Input class
8694  * @cfg {Boolean} disabled is it disabled
8695  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8696  * @cfg {String} name name of the input
8697  * @cfg {string} fieldLabel - the label associated
8698  * @cfg {string} placeholder - placeholder to put in text.
8699  * @cfg {string}  before - input group add on before
8700  * @cfg {string} after - input group add on after
8701  * @cfg {string} size - (lg|sm) or leave empty..
8702  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8703  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8704  * @cfg {Number} md colspan out of 12 for computer-sized screens
8705  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8706  * @cfg {string} value default value of the input
8707  * @cfg {Number} labelWidth set the width of label 
8708  * @cfg {Number} labellg set the width of label (1-12)
8709  * @cfg {Number} labelmd set the width of label (1-12)
8710  * @cfg {Number} labelsm set the width of label (1-12)
8711  * @cfg {Number} labelxs set the width of label (1-12)
8712  * @cfg {String} labelAlign (top|left)
8713  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8714  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8715  * @cfg {String} indicatorpos (left|right) default left
8716  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8717  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8718
8719  * @cfg {String} align (left|center|right) Default left
8720  * @cfg {Boolean} forceFeedback (true|false) Default false
8721  * 
8722  * @constructor
8723  * Create a new Input
8724  * @param {Object} config The config object
8725  */
8726
8727 Roo.bootstrap.Input = function(config){
8728     
8729     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8730     
8731     this.addEvents({
8732         /**
8733          * @event focus
8734          * Fires when this field receives input focus.
8735          * @param {Roo.form.Field} this
8736          */
8737         focus : true,
8738         /**
8739          * @event blur
8740          * Fires when this field loses input focus.
8741          * @param {Roo.form.Field} this
8742          */
8743         blur : true,
8744         /**
8745          * @event specialkey
8746          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8747          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8748          * @param {Roo.form.Field} this
8749          * @param {Roo.EventObject} e The event object
8750          */
8751         specialkey : true,
8752         /**
8753          * @event change
8754          * Fires just before the field blurs if the field value has changed.
8755          * @param {Roo.form.Field} this
8756          * @param {Mixed} newValue The new value
8757          * @param {Mixed} oldValue The original value
8758          */
8759         change : true,
8760         /**
8761          * @event invalid
8762          * Fires after the field has been marked as invalid.
8763          * @param {Roo.form.Field} this
8764          * @param {String} msg The validation message
8765          */
8766         invalid : true,
8767         /**
8768          * @event valid
8769          * Fires after the field has been validated with no errors.
8770          * @param {Roo.form.Field} this
8771          */
8772         valid : true,
8773          /**
8774          * @event keyup
8775          * Fires after the key up
8776          * @param {Roo.form.Field} this
8777          * @param {Roo.EventObject}  e The event Object
8778          */
8779         keyup : true
8780     });
8781 };
8782
8783 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8784      /**
8785      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8786       automatic validation (defaults to "keyup").
8787      */
8788     validationEvent : "keyup",
8789      /**
8790      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8791      */
8792     validateOnBlur : true,
8793     /**
8794      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8795      */
8796     validationDelay : 250,
8797      /**
8798      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8799      */
8800     focusClass : "x-form-focus",  // not needed???
8801     
8802        
8803     /**
8804      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8805      */
8806     invalidClass : "has-warning",
8807     
8808     /**
8809      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8810      */
8811     validClass : "has-success",
8812     
8813     /**
8814      * @cfg {Boolean} hasFeedback (true|false) default true
8815      */
8816     hasFeedback : true,
8817     
8818     /**
8819      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8820      */
8821     invalidFeedbackClass : "glyphicon-warning-sign",
8822     
8823     /**
8824      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8825      */
8826     validFeedbackClass : "glyphicon-ok",
8827     
8828     /**
8829      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8830      */
8831     selectOnFocus : false,
8832     
8833      /**
8834      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8835      */
8836     maskRe : null,
8837        /**
8838      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8839      */
8840     vtype : null,
8841     
8842       /**
8843      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8844      */
8845     disableKeyFilter : false,
8846     
8847        /**
8848      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8849      */
8850     disabled : false,
8851      /**
8852      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8853      */
8854     allowBlank : true,
8855     /**
8856      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8857      */
8858     blankText : "Please complete this mandatory field",
8859     
8860      /**
8861      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8862      */
8863     minLength : 0,
8864     /**
8865      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8866      */
8867     maxLength : Number.MAX_VALUE,
8868     /**
8869      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8870      */
8871     minLengthText : "The minimum length for this field is {0}",
8872     /**
8873      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8874      */
8875     maxLengthText : "The maximum length for this field is {0}",
8876   
8877     
8878     /**
8879      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8880      * If available, this function will be called only after the basic validators all return true, and will be passed the
8881      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8882      */
8883     validator : null,
8884     /**
8885      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8886      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8887      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8888      */
8889     regex : null,
8890     /**
8891      * @cfg {String} regexText -- Depricated - use Invalid Text
8892      */
8893     regexText : "",
8894     
8895     /**
8896      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8897      */
8898     invalidText : "",
8899     
8900     
8901     
8902     autocomplete: false,
8903     
8904     
8905     fieldLabel : '',
8906     inputType : 'text',
8907     
8908     name : false,
8909     placeholder: false,
8910     before : false,
8911     after : false,
8912     size : false,
8913     hasFocus : false,
8914     preventMark: false,
8915     isFormField : true,
8916     value : '',
8917     labelWidth : 2,
8918     labelAlign : false,
8919     readOnly : false,
8920     align : false,
8921     formatedValue : false,
8922     forceFeedback : false,
8923     
8924     indicatorpos : 'left',
8925     
8926     labellg : 0,
8927     labelmd : 0,
8928     labelsm : 0,
8929     labelxs : 0,
8930     
8931     capture : '',
8932     accept : '',
8933     
8934     parentLabelAlign : function()
8935     {
8936         var parent = this;
8937         while (parent.parent()) {
8938             parent = parent.parent();
8939             if (typeof(parent.labelAlign) !='undefined') {
8940                 return parent.labelAlign;
8941             }
8942         }
8943         return 'left';
8944         
8945     },
8946     
8947     getAutoCreate : function()
8948     {
8949         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8950         
8951         var id = Roo.id();
8952         
8953         var cfg = {};
8954         
8955         if(this.inputType != 'hidden'){
8956             cfg.cls = 'form-group' //input-group
8957         }
8958         
8959         var input =  {
8960             tag: 'input',
8961             id : id,
8962             type : this.inputType,
8963             value : this.value,
8964             cls : 'form-control',
8965             placeholder : this.placeholder || '',
8966             autocomplete : this.autocomplete || 'new-password'
8967         };
8968         
8969         if(this.capture.length){
8970             input.capture = this.capture;
8971         }
8972         
8973         if(this.accept.length){
8974             input.accept = this.accept + "/*";
8975         }
8976         
8977         if(this.align){
8978             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8979         }
8980         
8981         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8982             input.maxLength = this.maxLength;
8983         }
8984         
8985         if (this.disabled) {
8986             input.disabled=true;
8987         }
8988         
8989         if (this.readOnly) {
8990             input.readonly=true;
8991         }
8992         
8993         if (this.name) {
8994             input.name = this.name;
8995         }
8996         
8997         if (this.size) {
8998             input.cls += ' input-' + this.size;
8999         }
9000         
9001         var settings=this;
9002         ['xs','sm','md','lg'].map(function(size){
9003             if (settings[size]) {
9004                 cfg.cls += ' col-' + size + '-' + settings[size];
9005             }
9006         });
9007         
9008         var inputblock = input;
9009         
9010         var feedback = {
9011             tag: 'span',
9012             cls: 'glyphicon form-control-feedback'
9013         };
9014             
9015         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9016             
9017             inputblock = {
9018                 cls : 'has-feedback',
9019                 cn :  [
9020                     input,
9021                     feedback
9022                 ] 
9023             };  
9024         }
9025         
9026         if (this.before || this.after) {
9027             
9028             inputblock = {
9029                 cls : 'input-group',
9030                 cn :  [] 
9031             };
9032             
9033             if (this.before && typeof(this.before) == 'string') {
9034                 
9035                 inputblock.cn.push({
9036                     tag :'span',
9037                     cls : 'roo-input-before input-group-addon',
9038                     html : this.before
9039                 });
9040             }
9041             if (this.before && typeof(this.before) == 'object') {
9042                 this.before = Roo.factory(this.before);
9043                 
9044                 inputblock.cn.push({
9045                     tag :'span',
9046                     cls : 'roo-input-before input-group-' +
9047                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9048                 });
9049             }
9050             
9051             inputblock.cn.push(input);
9052             
9053             if (this.after && typeof(this.after) == 'string') {
9054                 inputblock.cn.push({
9055                     tag :'span',
9056                     cls : 'roo-input-after input-group-addon',
9057                     html : this.after
9058                 });
9059             }
9060             if (this.after && typeof(this.after) == 'object') {
9061                 this.after = Roo.factory(this.after);
9062                 
9063                 inputblock.cn.push({
9064                     tag :'span',
9065                     cls : 'roo-input-after input-group-' +
9066                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9067                 });
9068             }
9069             
9070             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9071                 inputblock.cls += ' has-feedback';
9072                 inputblock.cn.push(feedback);
9073             }
9074         };
9075         
9076         if (align ==='left' && this.fieldLabel.length) {
9077             
9078             cfg.cls += ' roo-form-group-label-left';
9079             
9080             cfg.cn = [
9081                 {
9082                     tag : 'i',
9083                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9084                     tooltip : 'This field is required'
9085                 },
9086                 {
9087                     tag: 'label',
9088                     'for' :  id,
9089                     cls : 'control-label',
9090                     html : this.fieldLabel
9091
9092                 },
9093                 {
9094                     cls : "", 
9095                     cn: [
9096                         inputblock
9097                     ]
9098                 }
9099             ];
9100             
9101             var labelCfg = cfg.cn[1];
9102             var contentCfg = cfg.cn[2];
9103             
9104             if(this.indicatorpos == 'right'){
9105                 cfg.cn = [
9106                     {
9107                         tag: 'label',
9108                         'for' :  id,
9109                         cls : 'control-label',
9110                         cn : [
9111                             {
9112                                 tag : 'span',
9113                                 html : this.fieldLabel
9114                             },
9115                             {
9116                                 tag : 'i',
9117                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9118                                 tooltip : 'This field is required'
9119                             }
9120                         ]
9121                     },
9122                     {
9123                         cls : "",
9124                         cn: [
9125                             inputblock
9126                         ]
9127                     }
9128
9129                 ];
9130                 
9131                 labelCfg = cfg.cn[0];
9132                 contentCfg = cfg.cn[1];
9133             
9134             }
9135             
9136             if(this.labelWidth > 12){
9137                 labelCfg.style = "width: " + this.labelWidth + 'px';
9138             }
9139             
9140             if(this.labelWidth < 13 && this.labelmd == 0){
9141                 this.labelmd = this.labelWidth;
9142             }
9143             
9144             if(this.labellg > 0){
9145                 labelCfg.cls += ' col-lg-' + this.labellg;
9146                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9147             }
9148             
9149             if(this.labelmd > 0){
9150                 labelCfg.cls += ' col-md-' + this.labelmd;
9151                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9152             }
9153             
9154             if(this.labelsm > 0){
9155                 labelCfg.cls += ' col-sm-' + this.labelsm;
9156                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9157             }
9158             
9159             if(this.labelxs > 0){
9160                 labelCfg.cls += ' col-xs-' + this.labelxs;
9161                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9162             }
9163             
9164             
9165         } else if ( this.fieldLabel.length) {
9166                 
9167             cfg.cn = [
9168                 {
9169                     tag : 'i',
9170                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9171                     tooltip : 'This field is required'
9172                 },
9173                 {
9174                     tag: 'label',
9175                    //cls : 'input-group-addon',
9176                     html : this.fieldLabel
9177
9178                 },
9179
9180                inputblock
9181
9182            ];
9183            
9184            if(this.indicatorpos == 'right'){
9185                 
9186                 cfg.cn = [
9187                     {
9188                         tag: 'label',
9189                        //cls : 'input-group-addon',
9190                         html : this.fieldLabel
9191
9192                     },
9193                     {
9194                         tag : 'i',
9195                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9196                         tooltip : 'This field is required'
9197                     },
9198
9199                    inputblock
9200
9201                ];
9202
9203             }
9204
9205         } else {
9206             
9207             cfg.cn = [
9208
9209                     inputblock
9210
9211             ];
9212                 
9213                 
9214         };
9215         
9216         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9217            cfg.cls += ' navbar-form';
9218         }
9219         
9220         if (this.parentType === 'NavGroup') {
9221            cfg.cls += ' navbar-form';
9222            cfg.tag = 'li';
9223         }
9224         
9225         return cfg;
9226         
9227     },
9228     /**
9229      * return the real input element.
9230      */
9231     inputEl: function ()
9232     {
9233         return this.el.select('input.form-control',true).first();
9234     },
9235     
9236     tooltipEl : function()
9237     {
9238         return this.inputEl();
9239     },
9240     
9241     indicatorEl : function()
9242     {
9243         var indicator = this.el.select('i.roo-required-indicator',true).first();
9244         
9245         if(!indicator){
9246             return false;
9247         }
9248         
9249         return indicator;
9250         
9251     },
9252     
9253     setDisabled : function(v)
9254     {
9255         var i  = this.inputEl().dom;
9256         if (!v) {
9257             i.removeAttribute('disabled');
9258             return;
9259             
9260         }
9261         i.setAttribute('disabled','true');
9262     },
9263     initEvents : function()
9264     {
9265           
9266         this.inputEl().on("keydown" , this.fireKey,  this);
9267         this.inputEl().on("focus", this.onFocus,  this);
9268         this.inputEl().on("blur", this.onBlur,  this);
9269         
9270         this.inputEl().relayEvent('keyup', this);
9271         
9272         this.indicator = this.indicatorEl();
9273         
9274         if(this.indicator){
9275             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9276         }
9277  
9278         // reference to original value for reset
9279         this.originalValue = this.getValue();
9280         //Roo.form.TextField.superclass.initEvents.call(this);
9281         if(this.validationEvent == 'keyup'){
9282             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9283             this.inputEl().on('keyup', this.filterValidation, this);
9284         }
9285         else if(this.validationEvent !== false){
9286             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9287         }
9288         
9289         if(this.selectOnFocus){
9290             this.on("focus", this.preFocus, this);
9291             
9292         }
9293         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9294             this.inputEl().on("keypress", this.filterKeys, this);
9295         } else {
9296             this.inputEl().relayEvent('keypress', this);
9297         }
9298        /* if(this.grow){
9299             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9300             this.el.on("click", this.autoSize,  this);
9301         }
9302         */
9303         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9304             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9305         }
9306         
9307         if (typeof(this.before) == 'object') {
9308             this.before.render(this.el.select('.roo-input-before',true).first());
9309         }
9310         if (typeof(this.after) == 'object') {
9311             this.after.render(this.el.select('.roo-input-after',true).first());
9312         }
9313         
9314         this.inputEl().on('change', this.onChange, this);
9315         
9316     },
9317     filterValidation : function(e){
9318         if(!e.isNavKeyPress()){
9319             this.validationTask.delay(this.validationDelay);
9320         }
9321     },
9322      /**
9323      * Validates the field value
9324      * @return {Boolean} True if the value is valid, else false
9325      */
9326     validate : function(){
9327         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9328         if(this.disabled || this.validateValue(this.getRawValue())){
9329             this.markValid();
9330             return true;
9331         }
9332         
9333         this.markInvalid();
9334         return false;
9335     },
9336     
9337     
9338     /**
9339      * Validates a value according to the field's validation rules and marks the field as invalid
9340      * if the validation fails
9341      * @param {Mixed} value The value to validate
9342      * @return {Boolean} True if the value is valid, else false
9343      */
9344     validateValue : function(value)
9345     {
9346         if(this.getVisibilityEl().hasClass('hidden')){
9347             return true;
9348         }
9349         
9350         if(value.length < 1)  { // if it's blank
9351             if(this.allowBlank){
9352                 return true;
9353             }
9354             return false;
9355         }
9356         
9357         if(value.length < this.minLength){
9358             return false;
9359         }
9360         if(value.length > this.maxLength){
9361             return false;
9362         }
9363         if(this.vtype){
9364             var vt = Roo.form.VTypes;
9365             if(!vt[this.vtype](value, this)){
9366                 return false;
9367             }
9368         }
9369         if(typeof this.validator == "function"){
9370             var msg = this.validator(value);
9371             if(msg !== true){
9372                 return false;
9373             }
9374             if (typeof(msg) == 'string') {
9375                 this.invalidText = msg;
9376             }
9377         }
9378         
9379         if(this.regex && !this.regex.test(value)){
9380             return false;
9381         }
9382         
9383         return true;
9384     },
9385     
9386      // private
9387     fireKey : function(e){
9388         //Roo.log('field ' + e.getKey());
9389         if(e.isNavKeyPress()){
9390             this.fireEvent("specialkey", this, e);
9391         }
9392     },
9393     focus : function (selectText){
9394         if(this.rendered){
9395             this.inputEl().focus();
9396             if(selectText === true){
9397                 this.inputEl().dom.select();
9398             }
9399         }
9400         return this;
9401     } ,
9402     
9403     onFocus : function(){
9404         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9405            // this.el.addClass(this.focusClass);
9406         }
9407         if(!this.hasFocus){
9408             this.hasFocus = true;
9409             this.startValue = this.getValue();
9410             this.fireEvent("focus", this);
9411         }
9412     },
9413     
9414     beforeBlur : Roo.emptyFn,
9415
9416     
9417     // private
9418     onBlur : function(){
9419         this.beforeBlur();
9420         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9421             //this.el.removeClass(this.focusClass);
9422         }
9423         this.hasFocus = false;
9424         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9425             this.validate();
9426         }
9427         var v = this.getValue();
9428         if(String(v) !== String(this.startValue)){
9429             this.fireEvent('change', this, v, this.startValue);
9430         }
9431         this.fireEvent("blur", this);
9432     },
9433     
9434     onChange : function(e)
9435     {
9436         var v = this.getValue();
9437         if(String(v) !== String(this.startValue)){
9438             this.fireEvent('change', this, v, this.startValue);
9439         }
9440         
9441     },
9442     
9443     /**
9444      * Resets the current field value to the originally loaded value and clears any validation messages
9445      */
9446     reset : function(){
9447         this.setValue(this.originalValue);
9448         this.validate();
9449     },
9450      /**
9451      * Returns the name of the field
9452      * @return {Mixed} name The name field
9453      */
9454     getName: function(){
9455         return this.name;
9456     },
9457      /**
9458      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9459      * @return {Mixed} value The field value
9460      */
9461     getValue : function(){
9462         
9463         var v = this.inputEl().getValue();
9464         
9465         return v;
9466     },
9467     /**
9468      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9469      * @return {Mixed} value The field value
9470      */
9471     getRawValue : function(){
9472         var v = this.inputEl().getValue();
9473         
9474         return v;
9475     },
9476     
9477     /**
9478      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9479      * @param {Mixed} value The value to set
9480      */
9481     setRawValue : function(v){
9482         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9483     },
9484     
9485     selectText : function(start, end){
9486         var v = this.getRawValue();
9487         if(v.length > 0){
9488             start = start === undefined ? 0 : start;
9489             end = end === undefined ? v.length : end;
9490             var d = this.inputEl().dom;
9491             if(d.setSelectionRange){
9492                 d.setSelectionRange(start, end);
9493             }else if(d.createTextRange){
9494                 var range = d.createTextRange();
9495                 range.moveStart("character", start);
9496                 range.moveEnd("character", v.length-end);
9497                 range.select();
9498             }
9499         }
9500     },
9501     
9502     /**
9503      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9504      * @param {Mixed} value The value to set
9505      */
9506     setValue : function(v){
9507         this.value = v;
9508         if(this.rendered){
9509             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9510             this.validate();
9511         }
9512     },
9513     
9514     /*
9515     processValue : function(value){
9516         if(this.stripCharsRe){
9517             var newValue = value.replace(this.stripCharsRe, '');
9518             if(newValue !== value){
9519                 this.setRawValue(newValue);
9520                 return newValue;
9521             }
9522         }
9523         return value;
9524     },
9525   */
9526     preFocus : function(){
9527         
9528         if(this.selectOnFocus){
9529             this.inputEl().dom.select();
9530         }
9531     },
9532     filterKeys : function(e){
9533         var k = e.getKey();
9534         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9535             return;
9536         }
9537         var c = e.getCharCode(), cc = String.fromCharCode(c);
9538         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9539             return;
9540         }
9541         if(!this.maskRe.test(cc)){
9542             e.stopEvent();
9543         }
9544     },
9545      /**
9546      * Clear any invalid styles/messages for this field
9547      */
9548     clearInvalid : function(){
9549         
9550         if(!this.el || this.preventMark){ // not rendered
9551             return;
9552         }
9553         
9554      
9555         this.el.removeClass(this.invalidClass);
9556         
9557         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9558             
9559             var feedback = this.el.select('.form-control-feedback', true).first();
9560             
9561             if(feedback){
9562                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9563             }
9564             
9565         }
9566         
9567         if(this.indicator){
9568             this.indicator.removeClass('visible');
9569             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9570         }
9571         
9572         this.fireEvent('valid', this);
9573     },
9574     
9575      /**
9576      * Mark this field as valid
9577      */
9578     markValid : function()
9579     {
9580         if(!this.el  || this.preventMark){ // not rendered...
9581             return;
9582         }
9583         
9584         this.el.removeClass([this.invalidClass, this.validClass]);
9585         
9586         var feedback = this.el.select('.form-control-feedback', true).first();
9587             
9588         if(feedback){
9589             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9590         }
9591         
9592         if(this.indicator){
9593             this.indicator.removeClass('visible');
9594             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9595         }
9596         
9597         if(this.disabled){
9598             return;
9599         }
9600         
9601         if(this.allowBlank && !this.getRawValue().length){
9602             return;
9603         }
9604         
9605         this.el.addClass(this.validClass);
9606         
9607         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9608             
9609             var feedback = this.el.select('.form-control-feedback', true).first();
9610             
9611             if(feedback){
9612                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9613                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9614             }
9615             
9616         }
9617         
9618         this.fireEvent('valid', this);
9619     },
9620     
9621      /**
9622      * Mark this field as invalid
9623      * @param {String} msg The validation message
9624      */
9625     markInvalid : function(msg)
9626     {
9627         if(!this.el  || this.preventMark){ // not rendered
9628             return;
9629         }
9630         
9631         this.el.removeClass([this.invalidClass, this.validClass]);
9632         
9633         var feedback = this.el.select('.form-control-feedback', true).first();
9634             
9635         if(feedback){
9636             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9637         }
9638
9639         if(this.disabled){
9640             return;
9641         }
9642         
9643         if(this.allowBlank && !this.getRawValue().length){
9644             return;
9645         }
9646         
9647         if(this.indicator){
9648             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9649             this.indicator.addClass('visible');
9650         }
9651         
9652         this.el.addClass(this.invalidClass);
9653         
9654         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9655             
9656             var feedback = this.el.select('.form-control-feedback', true).first();
9657             
9658             if(feedback){
9659                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9660                 
9661                 if(this.getValue().length || this.forceFeedback){
9662                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9663                 }
9664                 
9665             }
9666             
9667         }
9668         
9669         this.fireEvent('invalid', this, msg);
9670     },
9671     // private
9672     SafariOnKeyDown : function(event)
9673     {
9674         // this is a workaround for a password hang bug on chrome/ webkit.
9675         if (this.inputEl().dom.type != 'password') {
9676             return;
9677         }
9678         
9679         var isSelectAll = false;
9680         
9681         if(this.inputEl().dom.selectionEnd > 0){
9682             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9683         }
9684         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9685             event.preventDefault();
9686             this.setValue('');
9687             return;
9688         }
9689         
9690         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9691             
9692             event.preventDefault();
9693             // this is very hacky as keydown always get's upper case.
9694             //
9695             var cc = String.fromCharCode(event.getCharCode());
9696             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9697             
9698         }
9699     },
9700     adjustWidth : function(tag, w){
9701         tag = tag.toLowerCase();
9702         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9703             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9704                 if(tag == 'input'){
9705                     return w + 2;
9706                 }
9707                 if(tag == 'textarea'){
9708                     return w-2;
9709                 }
9710             }else if(Roo.isOpera){
9711                 if(tag == 'input'){
9712                     return w + 2;
9713                 }
9714                 if(tag == 'textarea'){
9715                     return w-2;
9716                 }
9717             }
9718         }
9719         return w;
9720     },
9721     
9722     setFieldLabel : function(v)
9723     {
9724         if(!this.rendered){
9725             return;
9726         }
9727         
9728         if(this.indicator){
9729             var ar = this.el.select('label > span',true);
9730             
9731             if (ar.elements.length) {
9732                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9733                 this.fieldLabel = v;
9734                 return;
9735             }
9736             
9737             var br = this.el.select('label',true);
9738             
9739             if(br.elements.length) {
9740                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9741                 this.fieldLabel = v;
9742                 return;
9743             }
9744             
9745             Roo.log('Cannot Found any of label > span || label in input');
9746             return;
9747         }
9748         
9749         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9750         this.fieldLabel = v;
9751         
9752         
9753     }
9754 });
9755
9756  
9757 /*
9758  * - LGPL
9759  *
9760  * Input
9761  * 
9762  */
9763
9764 /**
9765  * @class Roo.bootstrap.TextArea
9766  * @extends Roo.bootstrap.Input
9767  * Bootstrap TextArea class
9768  * @cfg {Number} cols Specifies the visible width of a text area
9769  * @cfg {Number} rows Specifies the visible number of lines in a text area
9770  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9771  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9772  * @cfg {string} html text
9773  * 
9774  * @constructor
9775  * Create a new TextArea
9776  * @param {Object} config The config object
9777  */
9778
9779 Roo.bootstrap.TextArea = function(config){
9780     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9781    
9782 };
9783
9784 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9785      
9786     cols : false,
9787     rows : 5,
9788     readOnly : false,
9789     warp : 'soft',
9790     resize : false,
9791     value: false,
9792     html: false,
9793     
9794     getAutoCreate : function(){
9795         
9796         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9797         
9798         var id = Roo.id();
9799         
9800         var cfg = {};
9801         
9802         if(this.inputType != 'hidden'){
9803             cfg.cls = 'form-group' //input-group
9804         }
9805         
9806         var input =  {
9807             tag: 'textarea',
9808             id : id,
9809             warp : this.warp,
9810             rows : this.rows,
9811             value : this.value || '',
9812             html: this.html || '',
9813             cls : 'form-control',
9814             placeholder : this.placeholder || '' 
9815             
9816         };
9817         
9818         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9819             input.maxLength = this.maxLength;
9820         }
9821         
9822         if(this.resize){
9823             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9824         }
9825         
9826         if(this.cols){
9827             input.cols = this.cols;
9828         }
9829         
9830         if (this.readOnly) {
9831             input.readonly = true;
9832         }
9833         
9834         if (this.name) {
9835             input.name = this.name;
9836         }
9837         
9838         if (this.size) {
9839             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9840         }
9841         
9842         var settings=this;
9843         ['xs','sm','md','lg'].map(function(size){
9844             if (settings[size]) {
9845                 cfg.cls += ' col-' + size + '-' + settings[size];
9846             }
9847         });
9848         
9849         var inputblock = input;
9850         
9851         if(this.hasFeedback && !this.allowBlank){
9852             
9853             var feedback = {
9854                 tag: 'span',
9855                 cls: 'glyphicon form-control-feedback'
9856             };
9857
9858             inputblock = {
9859                 cls : 'has-feedback',
9860                 cn :  [
9861                     input,
9862                     feedback
9863                 ] 
9864             };  
9865         }
9866         
9867         
9868         if (this.before || this.after) {
9869             
9870             inputblock = {
9871                 cls : 'input-group',
9872                 cn :  [] 
9873             };
9874             if (this.before) {
9875                 inputblock.cn.push({
9876                     tag :'span',
9877                     cls : 'input-group-addon',
9878                     html : this.before
9879                 });
9880             }
9881             
9882             inputblock.cn.push(input);
9883             
9884             if(this.hasFeedback && !this.allowBlank){
9885                 inputblock.cls += ' has-feedback';
9886                 inputblock.cn.push(feedback);
9887             }
9888             
9889             if (this.after) {
9890                 inputblock.cn.push({
9891                     tag :'span',
9892                     cls : 'input-group-addon',
9893                     html : this.after
9894                 });
9895             }
9896             
9897         }
9898         
9899         if (align ==='left' && this.fieldLabel.length) {
9900             cfg.cn = [
9901                 {
9902                     tag: 'label',
9903                     'for' :  id,
9904                     cls : 'control-label',
9905                     html : this.fieldLabel
9906                 },
9907                 {
9908                     cls : "",
9909                     cn: [
9910                         inputblock
9911                     ]
9912                 }
9913
9914             ];
9915             
9916             if(this.labelWidth > 12){
9917                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9918             }
9919
9920             if(this.labelWidth < 13 && this.labelmd == 0){
9921                 this.labelmd = this.labelWidth;
9922             }
9923
9924             if(this.labellg > 0){
9925                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9926                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9927             }
9928
9929             if(this.labelmd > 0){
9930                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9931                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9932             }
9933
9934             if(this.labelsm > 0){
9935                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9936                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9937             }
9938
9939             if(this.labelxs > 0){
9940                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9941                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9942             }
9943             
9944         } else if ( this.fieldLabel.length) {
9945             cfg.cn = [
9946
9947                {
9948                    tag: 'label',
9949                    //cls : 'input-group-addon',
9950                    html : this.fieldLabel
9951
9952                },
9953
9954                inputblock
9955
9956            ];
9957
9958         } else {
9959
9960             cfg.cn = [
9961
9962                 inputblock
9963
9964             ];
9965                 
9966         }
9967         
9968         if (this.disabled) {
9969             input.disabled=true;
9970         }
9971         
9972         return cfg;
9973         
9974     },
9975     /**
9976      * return the real textarea element.
9977      */
9978     inputEl: function ()
9979     {
9980         return this.el.select('textarea.form-control',true).first();
9981     },
9982     
9983     /**
9984      * Clear any invalid styles/messages for this field
9985      */
9986     clearInvalid : function()
9987     {
9988         
9989         if(!this.el || this.preventMark){ // not rendered
9990             return;
9991         }
9992         
9993         var label = this.el.select('label', true).first();
9994         var icon = this.el.select('i.fa-star', true).first();
9995         
9996         if(label && icon){
9997             icon.remove();
9998         }
9999         
10000         this.el.removeClass(this.invalidClass);
10001         
10002         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10003             
10004             var feedback = this.el.select('.form-control-feedback', true).first();
10005             
10006             if(feedback){
10007                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10008             }
10009             
10010         }
10011         
10012         this.fireEvent('valid', this);
10013     },
10014     
10015      /**
10016      * Mark this field as valid
10017      */
10018     markValid : function()
10019     {
10020         if(!this.el  || this.preventMark){ // not rendered
10021             return;
10022         }
10023         
10024         this.el.removeClass([this.invalidClass, this.validClass]);
10025         
10026         var feedback = this.el.select('.form-control-feedback', true).first();
10027             
10028         if(feedback){
10029             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10030         }
10031
10032         if(this.disabled || this.allowBlank){
10033             return;
10034         }
10035         
10036         var label = this.el.select('label', true).first();
10037         var icon = this.el.select('i.fa-star', true).first();
10038         
10039         if(label && icon){
10040             icon.remove();
10041         }
10042         
10043         this.el.addClass(this.validClass);
10044         
10045         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10046             
10047             var feedback = this.el.select('.form-control-feedback', true).first();
10048             
10049             if(feedback){
10050                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10051                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10052             }
10053             
10054         }
10055         
10056         this.fireEvent('valid', this);
10057     },
10058     
10059      /**
10060      * Mark this field as invalid
10061      * @param {String} msg The validation message
10062      */
10063     markInvalid : function(msg)
10064     {
10065         if(!this.el  || this.preventMark){ // not rendered
10066             return;
10067         }
10068         
10069         this.el.removeClass([this.invalidClass, this.validClass]);
10070         
10071         var feedback = this.el.select('.form-control-feedback', true).first();
10072             
10073         if(feedback){
10074             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10075         }
10076
10077         if(this.disabled || this.allowBlank){
10078             return;
10079         }
10080         
10081         var label = this.el.select('label', true).first();
10082         var icon = this.el.select('i.fa-star', true).first();
10083         
10084         if(!this.getValue().length && label && !icon){
10085             this.el.createChild({
10086                 tag : 'i',
10087                 cls : 'text-danger fa fa-lg fa-star',
10088                 tooltip : 'This field is required',
10089                 style : 'margin-right:5px;'
10090             }, label, true);
10091         }
10092
10093         this.el.addClass(this.invalidClass);
10094         
10095         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10096             
10097             var feedback = this.el.select('.form-control-feedback', true).first();
10098             
10099             if(feedback){
10100                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10101                 
10102                 if(this.getValue().length || this.forceFeedback){
10103                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10104                 }
10105                 
10106             }
10107             
10108         }
10109         
10110         this.fireEvent('invalid', this, msg);
10111     }
10112 });
10113
10114  
10115 /*
10116  * - LGPL
10117  *
10118  * trigger field - base class for combo..
10119  * 
10120  */
10121  
10122 /**
10123  * @class Roo.bootstrap.TriggerField
10124  * @extends Roo.bootstrap.Input
10125  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10126  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10127  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10128  * for which you can provide a custom implementation.  For example:
10129  * <pre><code>
10130 var trigger = new Roo.bootstrap.TriggerField();
10131 trigger.onTriggerClick = myTriggerFn;
10132 trigger.applyTo('my-field');
10133 </code></pre>
10134  *
10135  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10136  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10137  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10138  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10139  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10140
10141  * @constructor
10142  * Create a new TriggerField.
10143  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10144  * to the base TextField)
10145  */
10146 Roo.bootstrap.TriggerField = function(config){
10147     this.mimicing = false;
10148     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10149 };
10150
10151 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10152     /**
10153      * @cfg {String} triggerClass A CSS class to apply to the trigger
10154      */
10155      /**
10156      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10157      */
10158     hideTrigger:false,
10159
10160     /**
10161      * @cfg {Boolean} removable (true|false) special filter default false
10162      */
10163     removable : false,
10164     
10165     /** @cfg {Boolean} grow @hide */
10166     /** @cfg {Number} growMin @hide */
10167     /** @cfg {Number} growMax @hide */
10168
10169     /**
10170      * @hide 
10171      * @method
10172      */
10173     autoSize: Roo.emptyFn,
10174     // private
10175     monitorTab : true,
10176     // private
10177     deferHeight : true,
10178
10179     
10180     actionMode : 'wrap',
10181     
10182     caret : false,
10183     
10184     
10185     getAutoCreate : function(){
10186        
10187         var align = this.labelAlign || this.parentLabelAlign();
10188         
10189         var id = Roo.id();
10190         
10191         var cfg = {
10192             cls: 'form-group' //input-group
10193         };
10194         
10195         
10196         var input =  {
10197             tag: 'input',
10198             id : id,
10199             type : this.inputType,
10200             cls : 'form-control',
10201             autocomplete: 'new-password',
10202             placeholder : this.placeholder || '' 
10203             
10204         };
10205         if (this.name) {
10206             input.name = this.name;
10207         }
10208         if (this.size) {
10209             input.cls += ' input-' + this.size;
10210         }
10211         
10212         if (this.disabled) {
10213             input.disabled=true;
10214         }
10215         
10216         var inputblock = input;
10217         
10218         if(this.hasFeedback && !this.allowBlank){
10219             
10220             var feedback = {
10221                 tag: 'span',
10222                 cls: 'glyphicon form-control-feedback'
10223             };
10224             
10225             if(this.removable && !this.editable && !this.tickable){
10226                 inputblock = {
10227                     cls : 'has-feedback',
10228                     cn :  [
10229                         inputblock,
10230                         {
10231                             tag: 'button',
10232                             html : 'x',
10233                             cls : 'roo-combo-removable-btn close'
10234                         },
10235                         feedback
10236                     ] 
10237                 };
10238             } else {
10239                 inputblock = {
10240                     cls : 'has-feedback',
10241                     cn :  [
10242                         inputblock,
10243                         feedback
10244                     ] 
10245                 };
10246             }
10247
10248         } else {
10249             if(this.removable && !this.editable && !this.tickable){
10250                 inputblock = {
10251                     cls : 'roo-removable',
10252                     cn :  [
10253                         inputblock,
10254                         {
10255                             tag: 'button',
10256                             html : 'x',
10257                             cls : 'roo-combo-removable-btn close'
10258                         }
10259                     ] 
10260                 };
10261             }
10262         }
10263         
10264         if (this.before || this.after) {
10265             
10266             inputblock = {
10267                 cls : 'input-group',
10268                 cn :  [] 
10269             };
10270             if (this.before) {
10271                 inputblock.cn.push({
10272                     tag :'span',
10273                     cls : 'input-group-addon',
10274                     html : this.before
10275                 });
10276             }
10277             
10278             inputblock.cn.push(input);
10279             
10280             if(this.hasFeedback && !this.allowBlank){
10281                 inputblock.cls += ' has-feedback';
10282                 inputblock.cn.push(feedback);
10283             }
10284             
10285             if (this.after) {
10286                 inputblock.cn.push({
10287                     tag :'span',
10288                     cls : 'input-group-addon',
10289                     html : this.after
10290                 });
10291             }
10292             
10293         };
10294         
10295         var box = {
10296             tag: 'div',
10297             cn: [
10298                 {
10299                     tag: 'input',
10300                     type : 'hidden',
10301                     cls: 'form-hidden-field'
10302                 },
10303                 inputblock
10304             ]
10305             
10306         };
10307         
10308         if(this.multiple){
10309             box = {
10310                 tag: 'div',
10311                 cn: [
10312                     {
10313                         tag: 'input',
10314                         type : 'hidden',
10315                         cls: 'form-hidden-field'
10316                     },
10317                     {
10318                         tag: 'ul',
10319                         cls: 'roo-select2-choices',
10320                         cn:[
10321                             {
10322                                 tag: 'li',
10323                                 cls: 'roo-select2-search-field',
10324                                 cn: [
10325
10326                                     inputblock
10327                                 ]
10328                             }
10329                         ]
10330                     }
10331                 ]
10332             }
10333         };
10334         
10335         var combobox = {
10336             cls: 'roo-select2-container input-group',
10337             cn: [
10338                 box
10339 //                {
10340 //                    tag: 'ul',
10341 //                    cls: 'typeahead typeahead-long dropdown-menu',
10342 //                    style: 'display:none'
10343 //                }
10344             ]
10345         };
10346         
10347         if(!this.multiple && this.showToggleBtn){
10348             
10349             var caret = {
10350                         tag: 'span',
10351                         cls: 'caret'
10352              };
10353             if (this.caret != false) {
10354                 caret = {
10355                      tag: 'i',
10356                      cls: 'fa fa-' + this.caret
10357                 };
10358                 
10359             }
10360             
10361             combobox.cn.push({
10362                 tag :'span',
10363                 cls : 'input-group-addon btn dropdown-toggle',
10364                 cn : [
10365                     caret,
10366                     {
10367                         tag: 'span',
10368                         cls: 'combobox-clear',
10369                         cn  : [
10370                             {
10371                                 tag : 'i',
10372                                 cls: 'icon-remove'
10373                             }
10374                         ]
10375                     }
10376                 ]
10377
10378             })
10379         }
10380         
10381         if(this.multiple){
10382             combobox.cls += ' roo-select2-container-multi';
10383         }
10384         
10385         if (align ==='left' && this.fieldLabel.length) {
10386             
10387             cfg.cls += ' roo-form-group-label-left';
10388
10389             cfg.cn = [
10390                 {
10391                     tag : 'i',
10392                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10393                     tooltip : 'This field is required'
10394                 },
10395                 {
10396                     tag: 'label',
10397                     'for' :  id,
10398                     cls : 'control-label',
10399                     html : this.fieldLabel
10400
10401                 },
10402                 {
10403                     cls : "", 
10404                     cn: [
10405                         combobox
10406                     ]
10407                 }
10408
10409             ];
10410             
10411             var labelCfg = cfg.cn[1];
10412             var contentCfg = cfg.cn[2];
10413             
10414             if(this.indicatorpos == 'right'){
10415                 cfg.cn = [
10416                     {
10417                         tag: 'label',
10418                         'for' :  id,
10419                         cls : 'control-label',
10420                         cn : [
10421                             {
10422                                 tag : 'span',
10423                                 html : this.fieldLabel
10424                             },
10425                             {
10426                                 tag : 'i',
10427                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10428                                 tooltip : 'This field is required'
10429                             }
10430                         ]
10431                     },
10432                     {
10433                         cls : "", 
10434                         cn: [
10435                             combobox
10436                         ]
10437                     }
10438
10439                 ];
10440                 
10441                 labelCfg = cfg.cn[0];
10442                 contentCfg = cfg.cn[1];
10443             }
10444             
10445             if(this.labelWidth > 12){
10446                 labelCfg.style = "width: " + this.labelWidth + 'px';
10447             }
10448             
10449             if(this.labelWidth < 13 && this.labelmd == 0){
10450                 this.labelmd = this.labelWidth;
10451             }
10452             
10453             if(this.labellg > 0){
10454                 labelCfg.cls += ' col-lg-' + this.labellg;
10455                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10456             }
10457             
10458             if(this.labelmd > 0){
10459                 labelCfg.cls += ' col-md-' + this.labelmd;
10460                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10461             }
10462             
10463             if(this.labelsm > 0){
10464                 labelCfg.cls += ' col-sm-' + this.labelsm;
10465                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10466             }
10467             
10468             if(this.labelxs > 0){
10469                 labelCfg.cls += ' col-xs-' + this.labelxs;
10470                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10471             }
10472             
10473         } else if ( this.fieldLabel.length) {
10474 //                Roo.log(" label");
10475             cfg.cn = [
10476                 {
10477                    tag : 'i',
10478                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10479                    tooltip : 'This field is required'
10480                },
10481                {
10482                    tag: 'label',
10483                    //cls : 'input-group-addon',
10484                    html : this.fieldLabel
10485
10486                },
10487
10488                combobox
10489
10490             ];
10491             
10492             if(this.indicatorpos == 'right'){
10493                 
10494                 cfg.cn = [
10495                     {
10496                        tag: 'label',
10497                        cn : [
10498                            {
10499                                tag : 'span',
10500                                html : this.fieldLabel
10501                            },
10502                            {
10503                               tag : 'i',
10504                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10505                               tooltip : 'This field is required'
10506                            }
10507                        ]
10508
10509                     },
10510                     combobox
10511
10512                 ];
10513
10514             }
10515
10516         } else {
10517             
10518 //                Roo.log(" no label && no align");
10519                 cfg = combobox
10520                      
10521                 
10522         }
10523         
10524         var settings=this;
10525         ['xs','sm','md','lg'].map(function(size){
10526             if (settings[size]) {
10527                 cfg.cls += ' col-' + size + '-' + settings[size];
10528             }
10529         });
10530         
10531         return cfg;
10532         
10533     },
10534     
10535     
10536     
10537     // private
10538     onResize : function(w, h){
10539 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10540 //        if(typeof w == 'number'){
10541 //            var x = w - this.trigger.getWidth();
10542 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10543 //            this.trigger.setStyle('left', x+'px');
10544 //        }
10545     },
10546
10547     // private
10548     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10549
10550     // private
10551     getResizeEl : function(){
10552         return this.inputEl();
10553     },
10554
10555     // private
10556     getPositionEl : function(){
10557         return this.inputEl();
10558     },
10559
10560     // private
10561     alignErrorIcon : function(){
10562         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10563     },
10564
10565     // private
10566     initEvents : function(){
10567         
10568         this.createList();
10569         
10570         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10571         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10572         if(!this.multiple && this.showToggleBtn){
10573             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10574             if(this.hideTrigger){
10575                 this.trigger.setDisplayed(false);
10576             }
10577             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10578         }
10579         
10580         if(this.multiple){
10581             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10582         }
10583         
10584         if(this.removable && !this.editable && !this.tickable){
10585             var close = this.closeTriggerEl();
10586             
10587             if(close){
10588                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10589                 close.on('click', this.removeBtnClick, this, close);
10590             }
10591         }
10592         
10593         //this.trigger.addClassOnOver('x-form-trigger-over');
10594         //this.trigger.addClassOnClick('x-form-trigger-click');
10595         
10596         //if(!this.width){
10597         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10598         //}
10599     },
10600     
10601     closeTriggerEl : function()
10602     {
10603         var close = this.el.select('.roo-combo-removable-btn', true).first();
10604         return close ? close : false;
10605     },
10606     
10607     removeBtnClick : function(e, h, el)
10608     {
10609         e.preventDefault();
10610         
10611         if(this.fireEvent("remove", this) !== false){
10612             this.reset();
10613             this.fireEvent("afterremove", this)
10614         }
10615     },
10616     
10617     createList : function()
10618     {
10619         this.list = Roo.get(document.body).createChild({
10620             tag: 'ul',
10621             cls: 'typeahead typeahead-long dropdown-menu',
10622             style: 'display:none'
10623         });
10624         
10625         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10626         
10627     },
10628
10629     // private
10630     initTrigger : function(){
10631        
10632     },
10633
10634     // private
10635     onDestroy : function(){
10636         if(this.trigger){
10637             this.trigger.removeAllListeners();
10638           //  this.trigger.remove();
10639         }
10640         //if(this.wrap){
10641         //    this.wrap.remove();
10642         //}
10643         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10644     },
10645
10646     // private
10647     onFocus : function(){
10648         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10649         /*
10650         if(!this.mimicing){
10651             this.wrap.addClass('x-trigger-wrap-focus');
10652             this.mimicing = true;
10653             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10654             if(this.monitorTab){
10655                 this.el.on("keydown", this.checkTab, this);
10656             }
10657         }
10658         */
10659     },
10660
10661     // private
10662     checkTab : function(e){
10663         if(e.getKey() == e.TAB){
10664             this.triggerBlur();
10665         }
10666     },
10667
10668     // private
10669     onBlur : function(){
10670         // do nothing
10671     },
10672
10673     // private
10674     mimicBlur : function(e, t){
10675         /*
10676         if(!this.wrap.contains(t) && this.validateBlur()){
10677             this.triggerBlur();
10678         }
10679         */
10680     },
10681
10682     // private
10683     triggerBlur : function(){
10684         this.mimicing = false;
10685         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10686         if(this.monitorTab){
10687             this.el.un("keydown", this.checkTab, this);
10688         }
10689         //this.wrap.removeClass('x-trigger-wrap-focus');
10690         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10691     },
10692
10693     // private
10694     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10695     validateBlur : function(e, t){
10696         return true;
10697     },
10698
10699     // private
10700     onDisable : function(){
10701         this.inputEl().dom.disabled = true;
10702         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10703         //if(this.wrap){
10704         //    this.wrap.addClass('x-item-disabled');
10705         //}
10706     },
10707
10708     // private
10709     onEnable : function(){
10710         this.inputEl().dom.disabled = false;
10711         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10712         //if(this.wrap){
10713         //    this.el.removeClass('x-item-disabled');
10714         //}
10715     },
10716
10717     // private
10718     onShow : function(){
10719         var ae = this.getActionEl();
10720         
10721         if(ae){
10722             ae.dom.style.display = '';
10723             ae.dom.style.visibility = 'visible';
10724         }
10725     },
10726
10727     // private
10728     
10729     onHide : function(){
10730         var ae = this.getActionEl();
10731         ae.dom.style.display = 'none';
10732     },
10733
10734     /**
10735      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10736      * by an implementing function.
10737      * @method
10738      * @param {EventObject} e
10739      */
10740     onTriggerClick : Roo.emptyFn
10741 });
10742  /*
10743  * Based on:
10744  * Ext JS Library 1.1.1
10745  * Copyright(c) 2006-2007, Ext JS, LLC.
10746  *
10747  * Originally Released Under LGPL - original licence link has changed is not relivant.
10748  *
10749  * Fork - LGPL
10750  * <script type="text/javascript">
10751  */
10752
10753
10754 /**
10755  * @class Roo.data.SortTypes
10756  * @singleton
10757  * Defines the default sorting (casting?) comparison functions used when sorting data.
10758  */
10759 Roo.data.SortTypes = {
10760     /**
10761      * Default sort that does nothing
10762      * @param {Mixed} s The value being converted
10763      * @return {Mixed} The comparison value
10764      */
10765     none : function(s){
10766         return s;
10767     },
10768     
10769     /**
10770      * The regular expression used to strip tags
10771      * @type {RegExp}
10772      * @property
10773      */
10774     stripTagsRE : /<\/?[^>]+>/gi,
10775     
10776     /**
10777      * Strips all HTML tags to sort on text only
10778      * @param {Mixed} s The value being converted
10779      * @return {String} The comparison value
10780      */
10781     asText : function(s){
10782         return String(s).replace(this.stripTagsRE, "");
10783     },
10784     
10785     /**
10786      * Strips all HTML tags to sort on text only - Case insensitive
10787      * @param {Mixed} s The value being converted
10788      * @return {String} The comparison value
10789      */
10790     asUCText : function(s){
10791         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10792     },
10793     
10794     /**
10795      * Case insensitive string
10796      * @param {Mixed} s The value being converted
10797      * @return {String} The comparison value
10798      */
10799     asUCString : function(s) {
10800         return String(s).toUpperCase();
10801     },
10802     
10803     /**
10804      * Date sorting
10805      * @param {Mixed} s The value being converted
10806      * @return {Number} The comparison value
10807      */
10808     asDate : function(s) {
10809         if(!s){
10810             return 0;
10811         }
10812         if(s instanceof Date){
10813             return s.getTime();
10814         }
10815         return Date.parse(String(s));
10816     },
10817     
10818     /**
10819      * Float sorting
10820      * @param {Mixed} s The value being converted
10821      * @return {Float} The comparison value
10822      */
10823     asFloat : function(s) {
10824         var val = parseFloat(String(s).replace(/,/g, ""));
10825         if(isNaN(val)) {
10826             val = 0;
10827         }
10828         return val;
10829     },
10830     
10831     /**
10832      * Integer sorting
10833      * @param {Mixed} s The value being converted
10834      * @return {Number} The comparison value
10835      */
10836     asInt : function(s) {
10837         var val = parseInt(String(s).replace(/,/g, ""));
10838         if(isNaN(val)) {
10839             val = 0;
10840         }
10841         return val;
10842     }
10843 };/*
10844  * Based on:
10845  * Ext JS Library 1.1.1
10846  * Copyright(c) 2006-2007, Ext JS, LLC.
10847  *
10848  * Originally Released Under LGPL - original licence link has changed is not relivant.
10849  *
10850  * Fork - LGPL
10851  * <script type="text/javascript">
10852  */
10853
10854 /**
10855 * @class Roo.data.Record
10856  * Instances of this class encapsulate both record <em>definition</em> information, and record
10857  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10858  * to access Records cached in an {@link Roo.data.Store} object.<br>
10859  * <p>
10860  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10861  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10862  * objects.<br>
10863  * <p>
10864  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10865  * @constructor
10866  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10867  * {@link #create}. The parameters are the same.
10868  * @param {Array} data An associative Array of data values keyed by the field name.
10869  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10870  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10871  * not specified an integer id is generated.
10872  */
10873 Roo.data.Record = function(data, id){
10874     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10875     this.data = data;
10876 };
10877
10878 /**
10879  * Generate a constructor for a specific record layout.
10880  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10881  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10882  * Each field definition object may contain the following properties: <ul>
10883  * <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,
10884  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10885  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10886  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10887  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10888  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10889  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10890  * this may be omitted.</p></li>
10891  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10892  * <ul><li>auto (Default, implies no conversion)</li>
10893  * <li>string</li>
10894  * <li>int</li>
10895  * <li>float</li>
10896  * <li>boolean</li>
10897  * <li>date</li></ul></p></li>
10898  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10899  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10900  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10901  * by the Reader into an object that will be stored in the Record. It is passed the
10902  * following parameters:<ul>
10903  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10904  * </ul></p></li>
10905  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10906  * </ul>
10907  * <br>usage:<br><pre><code>
10908 var TopicRecord = Roo.data.Record.create(
10909     {name: 'title', mapping: 'topic_title'},
10910     {name: 'author', mapping: 'username'},
10911     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10912     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10913     {name: 'lastPoster', mapping: 'user2'},
10914     {name: 'excerpt', mapping: 'post_text'}
10915 );
10916
10917 var myNewRecord = new TopicRecord({
10918     title: 'Do my job please',
10919     author: 'noobie',
10920     totalPosts: 1,
10921     lastPost: new Date(),
10922     lastPoster: 'Animal',
10923     excerpt: 'No way dude!'
10924 });
10925 myStore.add(myNewRecord);
10926 </code></pre>
10927  * @method create
10928  * @static
10929  */
10930 Roo.data.Record.create = function(o){
10931     var f = function(){
10932         f.superclass.constructor.apply(this, arguments);
10933     };
10934     Roo.extend(f, Roo.data.Record);
10935     var p = f.prototype;
10936     p.fields = new Roo.util.MixedCollection(false, function(field){
10937         return field.name;
10938     });
10939     for(var i = 0, len = o.length; i < len; i++){
10940         p.fields.add(new Roo.data.Field(o[i]));
10941     }
10942     f.getField = function(name){
10943         return p.fields.get(name);  
10944     };
10945     return f;
10946 };
10947
10948 Roo.data.Record.AUTO_ID = 1000;
10949 Roo.data.Record.EDIT = 'edit';
10950 Roo.data.Record.REJECT = 'reject';
10951 Roo.data.Record.COMMIT = 'commit';
10952
10953 Roo.data.Record.prototype = {
10954     /**
10955      * Readonly flag - true if this record has been modified.
10956      * @type Boolean
10957      */
10958     dirty : false,
10959     editing : false,
10960     error: null,
10961     modified: null,
10962
10963     // private
10964     join : function(store){
10965         this.store = store;
10966     },
10967
10968     /**
10969      * Set the named field to the specified value.
10970      * @param {String} name The name of the field to set.
10971      * @param {Object} value The value to set the field to.
10972      */
10973     set : function(name, value){
10974         if(this.data[name] == value){
10975             return;
10976         }
10977         this.dirty = true;
10978         if(!this.modified){
10979             this.modified = {};
10980         }
10981         if(typeof this.modified[name] == 'undefined'){
10982             this.modified[name] = this.data[name];
10983         }
10984         this.data[name] = value;
10985         if(!this.editing && this.store){
10986             this.store.afterEdit(this);
10987         }       
10988     },
10989
10990     /**
10991      * Get the value of the named field.
10992      * @param {String} name The name of the field to get the value of.
10993      * @return {Object} The value of the field.
10994      */
10995     get : function(name){
10996         return this.data[name]; 
10997     },
10998
10999     // private
11000     beginEdit : function(){
11001         this.editing = true;
11002         this.modified = {}; 
11003     },
11004
11005     // private
11006     cancelEdit : function(){
11007         this.editing = false;
11008         delete this.modified;
11009     },
11010
11011     // private
11012     endEdit : function(){
11013         this.editing = false;
11014         if(this.dirty && this.store){
11015             this.store.afterEdit(this);
11016         }
11017     },
11018
11019     /**
11020      * Usually called by the {@link Roo.data.Store} which owns the Record.
11021      * Rejects all changes made to the Record since either creation, or the last commit operation.
11022      * Modified fields are reverted to their original values.
11023      * <p>
11024      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11025      * of reject operations.
11026      */
11027     reject : function(){
11028         var m = this.modified;
11029         for(var n in m){
11030             if(typeof m[n] != "function"){
11031                 this.data[n] = m[n];
11032             }
11033         }
11034         this.dirty = false;
11035         delete this.modified;
11036         this.editing = false;
11037         if(this.store){
11038             this.store.afterReject(this);
11039         }
11040     },
11041
11042     /**
11043      * Usually called by the {@link Roo.data.Store} which owns the Record.
11044      * Commits all changes made to the Record since either creation, or the last commit operation.
11045      * <p>
11046      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11047      * of commit operations.
11048      */
11049     commit : function(){
11050         this.dirty = false;
11051         delete this.modified;
11052         this.editing = false;
11053         if(this.store){
11054             this.store.afterCommit(this);
11055         }
11056     },
11057
11058     // private
11059     hasError : function(){
11060         return this.error != null;
11061     },
11062
11063     // private
11064     clearError : function(){
11065         this.error = null;
11066     },
11067
11068     /**
11069      * Creates a copy of this record.
11070      * @param {String} id (optional) A new record id if you don't want to use this record's id
11071      * @return {Record}
11072      */
11073     copy : function(newId) {
11074         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11075     }
11076 };/*
11077  * Based on:
11078  * Ext JS Library 1.1.1
11079  * Copyright(c) 2006-2007, Ext JS, LLC.
11080  *
11081  * Originally Released Under LGPL - original licence link has changed is not relivant.
11082  *
11083  * Fork - LGPL
11084  * <script type="text/javascript">
11085  */
11086
11087
11088
11089 /**
11090  * @class Roo.data.Store
11091  * @extends Roo.util.Observable
11092  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11093  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11094  * <p>
11095  * 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
11096  * has no knowledge of the format of the data returned by the Proxy.<br>
11097  * <p>
11098  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11099  * instances from the data object. These records are cached and made available through accessor functions.
11100  * @constructor
11101  * Creates a new Store.
11102  * @param {Object} config A config object containing the objects needed for the Store to access data,
11103  * and read the data into Records.
11104  */
11105 Roo.data.Store = function(config){
11106     this.data = new Roo.util.MixedCollection(false);
11107     this.data.getKey = function(o){
11108         return o.id;
11109     };
11110     this.baseParams = {};
11111     // private
11112     this.paramNames = {
11113         "start" : "start",
11114         "limit" : "limit",
11115         "sort" : "sort",
11116         "dir" : "dir",
11117         "multisort" : "_multisort"
11118     };
11119
11120     if(config && config.data){
11121         this.inlineData = config.data;
11122         delete config.data;
11123     }
11124
11125     Roo.apply(this, config);
11126     
11127     if(this.reader){ // reader passed
11128         this.reader = Roo.factory(this.reader, Roo.data);
11129         this.reader.xmodule = this.xmodule || false;
11130         if(!this.recordType){
11131             this.recordType = this.reader.recordType;
11132         }
11133         if(this.reader.onMetaChange){
11134             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11135         }
11136     }
11137
11138     if(this.recordType){
11139         this.fields = this.recordType.prototype.fields;
11140     }
11141     this.modified = [];
11142
11143     this.addEvents({
11144         /**
11145          * @event datachanged
11146          * Fires when the data cache has changed, and a widget which is using this Store
11147          * as a Record cache should refresh its view.
11148          * @param {Store} this
11149          */
11150         datachanged : true,
11151         /**
11152          * @event metachange
11153          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11154          * @param {Store} this
11155          * @param {Object} meta The JSON metadata
11156          */
11157         metachange : true,
11158         /**
11159          * @event add
11160          * Fires when Records have been added to the Store
11161          * @param {Store} this
11162          * @param {Roo.data.Record[]} records The array of Records added
11163          * @param {Number} index The index at which the record(s) were added
11164          */
11165         add : true,
11166         /**
11167          * @event remove
11168          * Fires when a Record has been removed from the Store
11169          * @param {Store} this
11170          * @param {Roo.data.Record} record The Record that was removed
11171          * @param {Number} index The index at which the record was removed
11172          */
11173         remove : true,
11174         /**
11175          * @event update
11176          * Fires when a Record has been updated
11177          * @param {Store} this
11178          * @param {Roo.data.Record} record The Record that was updated
11179          * @param {String} operation The update operation being performed.  Value may be one of:
11180          * <pre><code>
11181  Roo.data.Record.EDIT
11182  Roo.data.Record.REJECT
11183  Roo.data.Record.COMMIT
11184          * </code></pre>
11185          */
11186         update : true,
11187         /**
11188          * @event clear
11189          * Fires when the data cache has been cleared.
11190          * @param {Store} this
11191          */
11192         clear : true,
11193         /**
11194          * @event beforeload
11195          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11196          * the load action will be canceled.
11197          * @param {Store} this
11198          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11199          */
11200         beforeload : true,
11201         /**
11202          * @event beforeloadadd
11203          * Fires after a new set of Records has been loaded.
11204          * @param {Store} this
11205          * @param {Roo.data.Record[]} records The Records that were loaded
11206          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11207          */
11208         beforeloadadd : true,
11209         /**
11210          * @event load
11211          * Fires after a new set of Records has been loaded, before they are added to the store.
11212          * @param {Store} this
11213          * @param {Roo.data.Record[]} records The Records that were loaded
11214          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11215          * @params {Object} return from reader
11216          */
11217         load : true,
11218         /**
11219          * @event loadexception
11220          * Fires if an exception occurs in the Proxy during loading.
11221          * Called with the signature of the Proxy's "loadexception" event.
11222          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11223          * 
11224          * @param {Proxy} 
11225          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11226          * @param {Object} load options 
11227          * @param {Object} jsonData from your request (normally this contains the Exception)
11228          */
11229         loadexception : true
11230     });
11231     
11232     if(this.proxy){
11233         this.proxy = Roo.factory(this.proxy, Roo.data);
11234         this.proxy.xmodule = this.xmodule || false;
11235         this.relayEvents(this.proxy,  ["loadexception"]);
11236     }
11237     this.sortToggle = {};
11238     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11239
11240     Roo.data.Store.superclass.constructor.call(this);
11241
11242     if(this.inlineData){
11243         this.loadData(this.inlineData);
11244         delete this.inlineData;
11245     }
11246 };
11247
11248 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11249      /**
11250     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11251     * without a remote query - used by combo/forms at present.
11252     */
11253     
11254     /**
11255     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11256     */
11257     /**
11258     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11259     */
11260     /**
11261     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11262     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11263     */
11264     /**
11265     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11266     * on any HTTP request
11267     */
11268     /**
11269     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11270     */
11271     /**
11272     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11273     */
11274     multiSort: false,
11275     /**
11276     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11277     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11278     */
11279     remoteSort : false,
11280
11281     /**
11282     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11283      * loaded or when a record is removed. (defaults to false).
11284     */
11285     pruneModifiedRecords : false,
11286
11287     // private
11288     lastOptions : null,
11289
11290     /**
11291      * Add Records to the Store and fires the add event.
11292      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11293      */
11294     add : function(records){
11295         records = [].concat(records);
11296         for(var i = 0, len = records.length; i < len; i++){
11297             records[i].join(this);
11298         }
11299         var index = this.data.length;
11300         this.data.addAll(records);
11301         this.fireEvent("add", this, records, index);
11302     },
11303
11304     /**
11305      * Remove a Record from the Store and fires the remove event.
11306      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11307      */
11308     remove : function(record){
11309         var index = this.data.indexOf(record);
11310         this.data.removeAt(index);
11311  
11312         if(this.pruneModifiedRecords){
11313             this.modified.remove(record);
11314         }
11315         this.fireEvent("remove", this, record, index);
11316     },
11317
11318     /**
11319      * Remove all Records from the Store and fires the clear event.
11320      */
11321     removeAll : function(){
11322         this.data.clear();
11323         if(this.pruneModifiedRecords){
11324             this.modified = [];
11325         }
11326         this.fireEvent("clear", this);
11327     },
11328
11329     /**
11330      * Inserts Records to the Store at the given index and fires the add event.
11331      * @param {Number} index The start index at which to insert the passed Records.
11332      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11333      */
11334     insert : function(index, records){
11335         records = [].concat(records);
11336         for(var i = 0, len = records.length; i < len; i++){
11337             this.data.insert(index, records[i]);
11338             records[i].join(this);
11339         }
11340         this.fireEvent("add", this, records, index);
11341     },
11342
11343     /**
11344      * Get the index within the cache of the passed Record.
11345      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11346      * @return {Number} The index of the passed Record. Returns -1 if not found.
11347      */
11348     indexOf : function(record){
11349         return this.data.indexOf(record);
11350     },
11351
11352     /**
11353      * Get the index within the cache of the Record with the passed id.
11354      * @param {String} id The id of the Record to find.
11355      * @return {Number} The index of the Record. Returns -1 if not found.
11356      */
11357     indexOfId : function(id){
11358         return this.data.indexOfKey(id);
11359     },
11360
11361     /**
11362      * Get the Record with the specified id.
11363      * @param {String} id The id of the Record to find.
11364      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11365      */
11366     getById : function(id){
11367         return this.data.key(id);
11368     },
11369
11370     /**
11371      * Get the Record at the specified index.
11372      * @param {Number} index The index of the Record to find.
11373      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11374      */
11375     getAt : function(index){
11376         return this.data.itemAt(index);
11377     },
11378
11379     /**
11380      * Returns a range of Records between specified indices.
11381      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11382      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11383      * @return {Roo.data.Record[]} An array of Records
11384      */
11385     getRange : function(start, end){
11386         return this.data.getRange(start, end);
11387     },
11388
11389     // private
11390     storeOptions : function(o){
11391         o = Roo.apply({}, o);
11392         delete o.callback;
11393         delete o.scope;
11394         this.lastOptions = o;
11395     },
11396
11397     /**
11398      * Loads the Record cache from the configured Proxy using the configured Reader.
11399      * <p>
11400      * If using remote paging, then the first load call must specify the <em>start</em>
11401      * and <em>limit</em> properties in the options.params property to establish the initial
11402      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11403      * <p>
11404      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11405      * and this call will return before the new data has been loaded. Perform any post-processing
11406      * in a callback function, or in a "load" event handler.</strong>
11407      * <p>
11408      * @param {Object} options An object containing properties which control loading options:<ul>
11409      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11410      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11411      * passed the following arguments:<ul>
11412      * <li>r : Roo.data.Record[]</li>
11413      * <li>options: Options object from the load call</li>
11414      * <li>success: Boolean success indicator</li></ul></li>
11415      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11416      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11417      * </ul>
11418      */
11419     load : function(options){
11420         options = options || {};
11421         if(this.fireEvent("beforeload", this, options) !== false){
11422             this.storeOptions(options);
11423             var p = Roo.apply(options.params || {}, this.baseParams);
11424             // if meta was not loaded from remote source.. try requesting it.
11425             if (!this.reader.metaFromRemote) {
11426                 p._requestMeta = 1;
11427             }
11428             if(this.sortInfo && this.remoteSort){
11429                 var pn = this.paramNames;
11430                 p[pn["sort"]] = this.sortInfo.field;
11431                 p[pn["dir"]] = this.sortInfo.direction;
11432             }
11433             if (this.multiSort) {
11434                 var pn = this.paramNames;
11435                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11436             }
11437             
11438             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11439         }
11440     },
11441
11442     /**
11443      * Reloads the Record cache from the configured Proxy using the configured Reader and
11444      * the options from the last load operation performed.
11445      * @param {Object} options (optional) An object containing properties which may override the options
11446      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11447      * the most recently used options are reused).
11448      */
11449     reload : function(options){
11450         this.load(Roo.applyIf(options||{}, this.lastOptions));
11451     },
11452
11453     // private
11454     // Called as a callback by the Reader during a load operation.
11455     loadRecords : function(o, options, success){
11456         if(!o || success === false){
11457             if(success !== false){
11458                 this.fireEvent("load", this, [], options, o);
11459             }
11460             if(options.callback){
11461                 options.callback.call(options.scope || this, [], options, false);
11462             }
11463             return;
11464         }
11465         // if data returned failure - throw an exception.
11466         if (o.success === false) {
11467             // show a message if no listener is registered.
11468             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11469                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11470             }
11471             // loadmask wil be hooked into this..
11472             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11473             return;
11474         }
11475         var r = o.records, t = o.totalRecords || r.length;
11476         
11477         this.fireEvent("beforeloadadd", this, r, options, o);
11478         
11479         if(!options || options.add !== true){
11480             if(this.pruneModifiedRecords){
11481                 this.modified = [];
11482             }
11483             for(var i = 0, len = r.length; i < len; i++){
11484                 r[i].join(this);
11485             }
11486             if(this.snapshot){
11487                 this.data = this.snapshot;
11488                 delete this.snapshot;
11489             }
11490             this.data.clear();
11491             this.data.addAll(r);
11492             this.totalLength = t;
11493             this.applySort();
11494             this.fireEvent("datachanged", this);
11495         }else{
11496             this.totalLength = Math.max(t, this.data.length+r.length);
11497             this.add(r);
11498         }
11499         
11500         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11501                 
11502             var e = new Roo.data.Record({});
11503
11504             e.set(this.parent.displayField, this.parent.emptyTitle);
11505             e.set(this.parent.valueField, '');
11506
11507             this.insert(0, e);
11508         }
11509             
11510         this.fireEvent("load", this, r, options, o);
11511         if(options.callback){
11512             options.callback.call(options.scope || this, r, options, true);
11513         }
11514     },
11515
11516
11517     /**
11518      * Loads data from a passed data block. A Reader which understands the format of the data
11519      * must have been configured in the constructor.
11520      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11521      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11522      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11523      */
11524     loadData : function(o, append){
11525         var r = this.reader.readRecords(o);
11526         this.loadRecords(r, {add: append}, true);
11527     },
11528
11529     /**
11530      * Gets the number of cached records.
11531      * <p>
11532      * <em>If using paging, this may not be the total size of the dataset. If the data object
11533      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11534      * the data set size</em>
11535      */
11536     getCount : function(){
11537         return this.data.length || 0;
11538     },
11539
11540     /**
11541      * Gets the total number of records in the dataset as returned by the server.
11542      * <p>
11543      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11544      * the dataset size</em>
11545      */
11546     getTotalCount : function(){
11547         return this.totalLength || 0;
11548     },
11549
11550     /**
11551      * Returns the sort state of the Store as an object with two properties:
11552      * <pre><code>
11553  field {String} The name of the field by which the Records are sorted
11554  direction {String} The sort order, "ASC" or "DESC"
11555      * </code></pre>
11556      */
11557     getSortState : function(){
11558         return this.sortInfo;
11559     },
11560
11561     // private
11562     applySort : function(){
11563         if(this.sortInfo && !this.remoteSort){
11564             var s = this.sortInfo, f = s.field;
11565             var st = this.fields.get(f).sortType;
11566             var fn = function(r1, r2){
11567                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11568                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11569             };
11570             this.data.sort(s.direction, fn);
11571             if(this.snapshot && this.snapshot != this.data){
11572                 this.snapshot.sort(s.direction, fn);
11573             }
11574         }
11575     },
11576
11577     /**
11578      * Sets the default sort column and order to be used by the next load operation.
11579      * @param {String} fieldName The name of the field to sort by.
11580      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11581      */
11582     setDefaultSort : function(field, dir){
11583         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11584     },
11585
11586     /**
11587      * Sort the Records.
11588      * If remote sorting is used, the sort is performed on the server, and the cache is
11589      * reloaded. If local sorting is used, the cache is sorted internally.
11590      * @param {String} fieldName The name of the field to sort by.
11591      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11592      */
11593     sort : function(fieldName, dir){
11594         var f = this.fields.get(fieldName);
11595         if(!dir){
11596             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11597             
11598             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11599                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11600             }else{
11601                 dir = f.sortDir;
11602             }
11603         }
11604         this.sortToggle[f.name] = dir;
11605         this.sortInfo = {field: f.name, direction: dir};
11606         if(!this.remoteSort){
11607             this.applySort();
11608             this.fireEvent("datachanged", this);
11609         }else{
11610             this.load(this.lastOptions);
11611         }
11612     },
11613
11614     /**
11615      * Calls the specified function for each of the Records in the cache.
11616      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11617      * Returning <em>false</em> aborts and exits the iteration.
11618      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11619      */
11620     each : function(fn, scope){
11621         this.data.each(fn, scope);
11622     },
11623
11624     /**
11625      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11626      * (e.g., during paging).
11627      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11628      */
11629     getModifiedRecords : function(){
11630         return this.modified;
11631     },
11632
11633     // private
11634     createFilterFn : function(property, value, anyMatch){
11635         if(!value.exec){ // not a regex
11636             value = String(value);
11637             if(value.length == 0){
11638                 return false;
11639             }
11640             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11641         }
11642         return function(r){
11643             return value.test(r.data[property]);
11644         };
11645     },
11646
11647     /**
11648      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11649      * @param {String} property A field on your records
11650      * @param {Number} start The record index to start at (defaults to 0)
11651      * @param {Number} end The last record index to include (defaults to length - 1)
11652      * @return {Number} The sum
11653      */
11654     sum : function(property, start, end){
11655         var rs = this.data.items, v = 0;
11656         start = start || 0;
11657         end = (end || end === 0) ? end : rs.length-1;
11658
11659         for(var i = start; i <= end; i++){
11660             v += (rs[i].data[property] || 0);
11661         }
11662         return v;
11663     },
11664
11665     /**
11666      * Filter the records by a specified property.
11667      * @param {String} field A field on your records
11668      * @param {String/RegExp} value Either a string that the field
11669      * should start with or a RegExp to test against the field
11670      * @param {Boolean} anyMatch True to match any part not just the beginning
11671      */
11672     filter : function(property, value, anyMatch){
11673         var fn = this.createFilterFn(property, value, anyMatch);
11674         return fn ? this.filterBy(fn) : this.clearFilter();
11675     },
11676
11677     /**
11678      * Filter by a function. The specified function will be called with each
11679      * record in this data source. If the function returns true the record is included,
11680      * otherwise it is filtered.
11681      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11682      * @param {Object} scope (optional) The scope of the function (defaults to this)
11683      */
11684     filterBy : function(fn, scope){
11685         this.snapshot = this.snapshot || this.data;
11686         this.data = this.queryBy(fn, scope||this);
11687         this.fireEvent("datachanged", this);
11688     },
11689
11690     /**
11691      * Query the records by a specified property.
11692      * @param {String} field A field on your records
11693      * @param {String/RegExp} value Either a string that the field
11694      * should start with or a RegExp to test against the field
11695      * @param {Boolean} anyMatch True to match any part not just the beginning
11696      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11697      */
11698     query : function(property, value, anyMatch){
11699         var fn = this.createFilterFn(property, value, anyMatch);
11700         return fn ? this.queryBy(fn) : this.data.clone();
11701     },
11702
11703     /**
11704      * Query by a function. The specified function will be called with each
11705      * record in this data source. If the function returns true the record is included
11706      * in the results.
11707      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11708      * @param {Object} scope (optional) The scope of the function (defaults to this)
11709       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11710      **/
11711     queryBy : function(fn, scope){
11712         var data = this.snapshot || this.data;
11713         return data.filterBy(fn, scope||this);
11714     },
11715
11716     /**
11717      * Collects unique values for a particular dataIndex from this store.
11718      * @param {String} dataIndex The property to collect
11719      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11720      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11721      * @return {Array} An array of the unique values
11722      **/
11723     collect : function(dataIndex, allowNull, bypassFilter){
11724         var d = (bypassFilter === true && this.snapshot) ?
11725                 this.snapshot.items : this.data.items;
11726         var v, sv, r = [], l = {};
11727         for(var i = 0, len = d.length; i < len; i++){
11728             v = d[i].data[dataIndex];
11729             sv = String(v);
11730             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11731                 l[sv] = true;
11732                 r[r.length] = v;
11733             }
11734         }
11735         return r;
11736     },
11737
11738     /**
11739      * Revert to a view of the Record cache with no filtering applied.
11740      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11741      */
11742     clearFilter : function(suppressEvent){
11743         if(this.snapshot && this.snapshot != this.data){
11744             this.data = this.snapshot;
11745             delete this.snapshot;
11746             if(suppressEvent !== true){
11747                 this.fireEvent("datachanged", this);
11748             }
11749         }
11750     },
11751
11752     // private
11753     afterEdit : function(record){
11754         if(this.modified.indexOf(record) == -1){
11755             this.modified.push(record);
11756         }
11757         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11758     },
11759     
11760     // private
11761     afterReject : function(record){
11762         this.modified.remove(record);
11763         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11764     },
11765
11766     // private
11767     afterCommit : function(record){
11768         this.modified.remove(record);
11769         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11770     },
11771
11772     /**
11773      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11774      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11775      */
11776     commitChanges : function(){
11777         var m = this.modified.slice(0);
11778         this.modified = [];
11779         for(var i = 0, len = m.length; i < len; i++){
11780             m[i].commit();
11781         }
11782     },
11783
11784     /**
11785      * Cancel outstanding changes on all changed records.
11786      */
11787     rejectChanges : function(){
11788         var m = this.modified.slice(0);
11789         this.modified = [];
11790         for(var i = 0, len = m.length; i < len; i++){
11791             m[i].reject();
11792         }
11793     },
11794
11795     onMetaChange : function(meta, rtype, o){
11796         this.recordType = rtype;
11797         this.fields = rtype.prototype.fields;
11798         delete this.snapshot;
11799         this.sortInfo = meta.sortInfo || this.sortInfo;
11800         this.modified = [];
11801         this.fireEvent('metachange', this, this.reader.meta);
11802     },
11803     
11804     moveIndex : function(data, type)
11805     {
11806         var index = this.indexOf(data);
11807         
11808         var newIndex = index + type;
11809         
11810         this.remove(data);
11811         
11812         this.insert(newIndex, data);
11813         
11814     }
11815 });/*
11816  * Based on:
11817  * Ext JS Library 1.1.1
11818  * Copyright(c) 2006-2007, Ext JS, LLC.
11819  *
11820  * Originally Released Under LGPL - original licence link has changed is not relivant.
11821  *
11822  * Fork - LGPL
11823  * <script type="text/javascript">
11824  */
11825
11826 /**
11827  * @class Roo.data.SimpleStore
11828  * @extends Roo.data.Store
11829  * Small helper class to make creating Stores from Array data easier.
11830  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11831  * @cfg {Array} fields An array of field definition objects, or field name strings.
11832  * @cfg {Array} data The multi-dimensional array of data
11833  * @constructor
11834  * @param {Object} config
11835  */
11836 Roo.data.SimpleStore = function(config){
11837     Roo.data.SimpleStore.superclass.constructor.call(this, {
11838         isLocal : true,
11839         reader: new Roo.data.ArrayReader({
11840                 id: config.id
11841             },
11842             Roo.data.Record.create(config.fields)
11843         ),
11844         proxy : new Roo.data.MemoryProxy(config.data)
11845     });
11846     this.load();
11847 };
11848 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11849  * Based on:
11850  * Ext JS Library 1.1.1
11851  * Copyright(c) 2006-2007, Ext JS, LLC.
11852  *
11853  * Originally Released Under LGPL - original licence link has changed is not relivant.
11854  *
11855  * Fork - LGPL
11856  * <script type="text/javascript">
11857  */
11858
11859 /**
11860 /**
11861  * @extends Roo.data.Store
11862  * @class Roo.data.JsonStore
11863  * Small helper class to make creating Stores for JSON data easier. <br/>
11864 <pre><code>
11865 var store = new Roo.data.JsonStore({
11866     url: 'get-images.php',
11867     root: 'images',
11868     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11869 });
11870 </code></pre>
11871  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11872  * JsonReader and HttpProxy (unless inline data is provided).</b>
11873  * @cfg {Array} fields An array of field definition objects, or field name strings.
11874  * @constructor
11875  * @param {Object} config
11876  */
11877 Roo.data.JsonStore = function(c){
11878     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11879         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11880         reader: new Roo.data.JsonReader(c, c.fields)
11881     }));
11882 };
11883 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11884  * Based on:
11885  * Ext JS Library 1.1.1
11886  * Copyright(c) 2006-2007, Ext JS, LLC.
11887  *
11888  * Originally Released Under LGPL - original licence link has changed is not relivant.
11889  *
11890  * Fork - LGPL
11891  * <script type="text/javascript">
11892  */
11893
11894  
11895 Roo.data.Field = function(config){
11896     if(typeof config == "string"){
11897         config = {name: config};
11898     }
11899     Roo.apply(this, config);
11900     
11901     if(!this.type){
11902         this.type = "auto";
11903     }
11904     
11905     var st = Roo.data.SortTypes;
11906     // named sortTypes are supported, here we look them up
11907     if(typeof this.sortType == "string"){
11908         this.sortType = st[this.sortType];
11909     }
11910     
11911     // set default sortType for strings and dates
11912     if(!this.sortType){
11913         switch(this.type){
11914             case "string":
11915                 this.sortType = st.asUCString;
11916                 break;
11917             case "date":
11918                 this.sortType = st.asDate;
11919                 break;
11920             default:
11921                 this.sortType = st.none;
11922         }
11923     }
11924
11925     // define once
11926     var stripRe = /[\$,%]/g;
11927
11928     // prebuilt conversion function for this field, instead of
11929     // switching every time we're reading a value
11930     if(!this.convert){
11931         var cv, dateFormat = this.dateFormat;
11932         switch(this.type){
11933             case "":
11934             case "auto":
11935             case undefined:
11936                 cv = function(v){ return v; };
11937                 break;
11938             case "string":
11939                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11940                 break;
11941             case "int":
11942                 cv = function(v){
11943                     return v !== undefined && v !== null && v !== '' ?
11944                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11945                     };
11946                 break;
11947             case "float":
11948                 cv = function(v){
11949                     return v !== undefined && v !== null && v !== '' ?
11950                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11951                     };
11952                 break;
11953             case "bool":
11954             case "boolean":
11955                 cv = function(v){ return v === true || v === "true" || v == 1; };
11956                 break;
11957             case "date":
11958                 cv = function(v){
11959                     if(!v){
11960                         return '';
11961                     }
11962                     if(v instanceof Date){
11963                         return v;
11964                     }
11965                     if(dateFormat){
11966                         if(dateFormat == "timestamp"){
11967                             return new Date(v*1000);
11968                         }
11969                         return Date.parseDate(v, dateFormat);
11970                     }
11971                     var parsed = Date.parse(v);
11972                     return parsed ? new Date(parsed) : null;
11973                 };
11974              break;
11975             
11976         }
11977         this.convert = cv;
11978     }
11979 };
11980
11981 Roo.data.Field.prototype = {
11982     dateFormat: null,
11983     defaultValue: "",
11984     mapping: null,
11985     sortType : null,
11986     sortDir : "ASC"
11987 };/*
11988  * Based on:
11989  * Ext JS Library 1.1.1
11990  * Copyright(c) 2006-2007, Ext JS, LLC.
11991  *
11992  * Originally Released Under LGPL - original licence link has changed is not relivant.
11993  *
11994  * Fork - LGPL
11995  * <script type="text/javascript">
11996  */
11997  
11998 // Base class for reading structured data from a data source.  This class is intended to be
11999 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12000
12001 /**
12002  * @class Roo.data.DataReader
12003  * Base class for reading structured data from a data source.  This class is intended to be
12004  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12005  */
12006
12007 Roo.data.DataReader = function(meta, recordType){
12008     
12009     this.meta = meta;
12010     
12011     this.recordType = recordType instanceof Array ? 
12012         Roo.data.Record.create(recordType) : recordType;
12013 };
12014
12015 Roo.data.DataReader.prototype = {
12016      /**
12017      * Create an empty record
12018      * @param {Object} data (optional) - overlay some values
12019      * @return {Roo.data.Record} record created.
12020      */
12021     newRow :  function(d) {
12022         var da =  {};
12023         this.recordType.prototype.fields.each(function(c) {
12024             switch( c.type) {
12025                 case 'int' : da[c.name] = 0; break;
12026                 case 'date' : da[c.name] = new Date(); break;
12027                 case 'float' : da[c.name] = 0.0; break;
12028                 case 'boolean' : da[c.name] = false; break;
12029                 default : da[c.name] = ""; break;
12030             }
12031             
12032         });
12033         return new this.recordType(Roo.apply(da, d));
12034     }
12035     
12036 };/*
12037  * Based on:
12038  * Ext JS Library 1.1.1
12039  * Copyright(c) 2006-2007, Ext JS, LLC.
12040  *
12041  * Originally Released Under LGPL - original licence link has changed is not relivant.
12042  *
12043  * Fork - LGPL
12044  * <script type="text/javascript">
12045  */
12046
12047 /**
12048  * @class Roo.data.DataProxy
12049  * @extends Roo.data.Observable
12050  * This class is an abstract base class for implementations which provide retrieval of
12051  * unformatted data objects.<br>
12052  * <p>
12053  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12054  * (of the appropriate type which knows how to parse the data object) to provide a block of
12055  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12056  * <p>
12057  * Custom implementations must implement the load method as described in
12058  * {@link Roo.data.HttpProxy#load}.
12059  */
12060 Roo.data.DataProxy = function(){
12061     this.addEvents({
12062         /**
12063          * @event beforeload
12064          * Fires before a network request is made to retrieve a data object.
12065          * @param {Object} This DataProxy object.
12066          * @param {Object} params The params parameter to the load function.
12067          */
12068         beforeload : true,
12069         /**
12070          * @event load
12071          * Fires before the load method's callback is called.
12072          * @param {Object} This DataProxy object.
12073          * @param {Object} o The data object.
12074          * @param {Object} arg The callback argument object passed to the load function.
12075          */
12076         load : true,
12077         /**
12078          * @event loadexception
12079          * Fires if an Exception occurs during data retrieval.
12080          * @param {Object} This DataProxy object.
12081          * @param {Object} o The data object.
12082          * @param {Object} arg The callback argument object passed to the load function.
12083          * @param {Object} e The Exception.
12084          */
12085         loadexception : true
12086     });
12087     Roo.data.DataProxy.superclass.constructor.call(this);
12088 };
12089
12090 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12091
12092     /**
12093      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12094      */
12095 /*
12096  * Based on:
12097  * Ext JS Library 1.1.1
12098  * Copyright(c) 2006-2007, Ext JS, LLC.
12099  *
12100  * Originally Released Under LGPL - original licence link has changed is not relivant.
12101  *
12102  * Fork - LGPL
12103  * <script type="text/javascript">
12104  */
12105 /**
12106  * @class Roo.data.MemoryProxy
12107  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12108  * to the Reader when its load method is called.
12109  * @constructor
12110  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12111  */
12112 Roo.data.MemoryProxy = function(data){
12113     if (data.data) {
12114         data = data.data;
12115     }
12116     Roo.data.MemoryProxy.superclass.constructor.call(this);
12117     this.data = data;
12118 };
12119
12120 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12121     
12122     /**
12123      * Load data from the requested source (in this case an in-memory
12124      * data object passed to the constructor), read the data object into
12125      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12126      * process that block using the passed callback.
12127      * @param {Object} params This parameter is not used by the MemoryProxy class.
12128      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12129      * object into a block of Roo.data.Records.
12130      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12131      * The function must be passed <ul>
12132      * <li>The Record block object</li>
12133      * <li>The "arg" argument from the load function</li>
12134      * <li>A boolean success indicator</li>
12135      * </ul>
12136      * @param {Object} scope The scope in which to call the callback
12137      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12138      */
12139     load : function(params, reader, callback, scope, arg){
12140         params = params || {};
12141         var result;
12142         try {
12143             result = reader.readRecords(this.data);
12144         }catch(e){
12145             this.fireEvent("loadexception", this, arg, null, e);
12146             callback.call(scope, null, arg, false);
12147             return;
12148         }
12149         callback.call(scope, result, arg, true);
12150     },
12151     
12152     // private
12153     update : function(params, records){
12154         
12155     }
12156 });/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166 /**
12167  * @class Roo.data.HttpProxy
12168  * @extends Roo.data.DataProxy
12169  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12170  * configured to reference a certain URL.<br><br>
12171  * <p>
12172  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12173  * from which the running page was served.<br><br>
12174  * <p>
12175  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12176  * <p>
12177  * Be aware that to enable the browser to parse an XML document, the server must set
12178  * the Content-Type header in the HTTP response to "text/xml".
12179  * @constructor
12180  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12181  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12182  * will be used to make the request.
12183  */
12184 Roo.data.HttpProxy = function(conn){
12185     Roo.data.HttpProxy.superclass.constructor.call(this);
12186     // is conn a conn config or a real conn?
12187     this.conn = conn;
12188     this.useAjax = !conn || !conn.events;
12189   
12190 };
12191
12192 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12193     // thse are take from connection...
12194     
12195     /**
12196      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12197      */
12198     /**
12199      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12200      * extra parameters to each request made by this object. (defaults to undefined)
12201      */
12202     /**
12203      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12204      *  to each request made by this object. (defaults to undefined)
12205      */
12206     /**
12207      * @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)
12208      */
12209     /**
12210      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12211      */
12212      /**
12213      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12214      * @type Boolean
12215      */
12216   
12217
12218     /**
12219      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12220      * @type Boolean
12221      */
12222     /**
12223      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12224      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12225      * a finer-grained basis than the DataProxy events.
12226      */
12227     getConnection : function(){
12228         return this.useAjax ? Roo.Ajax : this.conn;
12229     },
12230
12231     /**
12232      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12233      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12234      * process that block using the passed callback.
12235      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12236      * for the request to the remote server.
12237      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12238      * object into a block of Roo.data.Records.
12239      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12240      * The function must be passed <ul>
12241      * <li>The Record block object</li>
12242      * <li>The "arg" argument from the load function</li>
12243      * <li>A boolean success indicator</li>
12244      * </ul>
12245      * @param {Object} scope The scope in which to call the callback
12246      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12247      */
12248     load : function(params, reader, callback, scope, arg){
12249         if(this.fireEvent("beforeload", this, params) !== false){
12250             var  o = {
12251                 params : params || {},
12252                 request: {
12253                     callback : callback,
12254                     scope : scope,
12255                     arg : arg
12256                 },
12257                 reader: reader,
12258                 callback : this.loadResponse,
12259                 scope: this
12260             };
12261             if(this.useAjax){
12262                 Roo.applyIf(o, this.conn);
12263                 if(this.activeRequest){
12264                     Roo.Ajax.abort(this.activeRequest);
12265                 }
12266                 this.activeRequest = Roo.Ajax.request(o);
12267             }else{
12268                 this.conn.request(o);
12269             }
12270         }else{
12271             callback.call(scope||this, null, arg, false);
12272         }
12273     },
12274
12275     // private
12276     loadResponse : function(o, success, response){
12277         delete this.activeRequest;
12278         if(!success){
12279             this.fireEvent("loadexception", this, o, response);
12280             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12281             return;
12282         }
12283         var result;
12284         try {
12285             result = o.reader.read(response);
12286         }catch(e){
12287             this.fireEvent("loadexception", this, o, response, e);
12288             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12289             return;
12290         }
12291         
12292         this.fireEvent("load", this, o, o.request.arg);
12293         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12294     },
12295
12296     // private
12297     update : function(dataSet){
12298
12299     },
12300
12301     // private
12302     updateResponse : function(dataSet){
12303
12304     }
12305 });/*
12306  * Based on:
12307  * Ext JS Library 1.1.1
12308  * Copyright(c) 2006-2007, Ext JS, LLC.
12309  *
12310  * Originally Released Under LGPL - original licence link has changed is not relivant.
12311  *
12312  * Fork - LGPL
12313  * <script type="text/javascript">
12314  */
12315
12316 /**
12317  * @class Roo.data.ScriptTagProxy
12318  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12319  * other than the originating domain of the running page.<br><br>
12320  * <p>
12321  * <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
12322  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12323  * <p>
12324  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12325  * source code that is used as the source inside a &lt;script> tag.<br><br>
12326  * <p>
12327  * In order for the browser to process the returned data, the server must wrap the data object
12328  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12329  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12330  * depending on whether the callback name was passed:
12331  * <p>
12332  * <pre><code>
12333 boolean scriptTag = false;
12334 String cb = request.getParameter("callback");
12335 if (cb != null) {
12336     scriptTag = true;
12337     response.setContentType("text/javascript");
12338 } else {
12339     response.setContentType("application/x-json");
12340 }
12341 Writer out = response.getWriter();
12342 if (scriptTag) {
12343     out.write(cb + "(");
12344 }
12345 out.print(dataBlock.toJsonString());
12346 if (scriptTag) {
12347     out.write(");");
12348 }
12349 </pre></code>
12350  *
12351  * @constructor
12352  * @param {Object} config A configuration object.
12353  */
12354 Roo.data.ScriptTagProxy = function(config){
12355     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12356     Roo.apply(this, config);
12357     this.head = document.getElementsByTagName("head")[0];
12358 };
12359
12360 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12361
12362 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12363     /**
12364      * @cfg {String} url The URL from which to request the data object.
12365      */
12366     /**
12367      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12368      */
12369     timeout : 30000,
12370     /**
12371      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12372      * the server the name of the callback function set up by the load call to process the returned data object.
12373      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12374      * javascript output which calls this named function passing the data object as its only parameter.
12375      */
12376     callbackParam : "callback",
12377     /**
12378      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12379      * name to the request.
12380      */
12381     nocache : true,
12382
12383     /**
12384      * Load data from the configured URL, read the data object into
12385      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12386      * process that block using the passed callback.
12387      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12388      * for the request to the remote server.
12389      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12390      * object into a block of Roo.data.Records.
12391      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12392      * The function must be passed <ul>
12393      * <li>The Record block object</li>
12394      * <li>The "arg" argument from the load function</li>
12395      * <li>A boolean success indicator</li>
12396      * </ul>
12397      * @param {Object} scope The scope in which to call the callback
12398      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12399      */
12400     load : function(params, reader, callback, scope, arg){
12401         if(this.fireEvent("beforeload", this, params) !== false){
12402
12403             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12404
12405             var url = this.url;
12406             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12407             if(this.nocache){
12408                 url += "&_dc=" + (new Date().getTime());
12409             }
12410             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12411             var trans = {
12412                 id : transId,
12413                 cb : "stcCallback"+transId,
12414                 scriptId : "stcScript"+transId,
12415                 params : params,
12416                 arg : arg,
12417                 url : url,
12418                 callback : callback,
12419                 scope : scope,
12420                 reader : reader
12421             };
12422             var conn = this;
12423
12424             window[trans.cb] = function(o){
12425                 conn.handleResponse(o, trans);
12426             };
12427
12428             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12429
12430             if(this.autoAbort !== false){
12431                 this.abort();
12432             }
12433
12434             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12435
12436             var script = document.createElement("script");
12437             script.setAttribute("src", url);
12438             script.setAttribute("type", "text/javascript");
12439             script.setAttribute("id", trans.scriptId);
12440             this.head.appendChild(script);
12441
12442             this.trans = trans;
12443         }else{
12444             callback.call(scope||this, null, arg, false);
12445         }
12446     },
12447
12448     // private
12449     isLoading : function(){
12450         return this.trans ? true : false;
12451     },
12452
12453     /**
12454      * Abort the current server request.
12455      */
12456     abort : function(){
12457         if(this.isLoading()){
12458             this.destroyTrans(this.trans);
12459         }
12460     },
12461
12462     // private
12463     destroyTrans : function(trans, isLoaded){
12464         this.head.removeChild(document.getElementById(trans.scriptId));
12465         clearTimeout(trans.timeoutId);
12466         if(isLoaded){
12467             window[trans.cb] = undefined;
12468             try{
12469                 delete window[trans.cb];
12470             }catch(e){}
12471         }else{
12472             // if hasn't been loaded, wait for load to remove it to prevent script error
12473             window[trans.cb] = function(){
12474                 window[trans.cb] = undefined;
12475                 try{
12476                     delete window[trans.cb];
12477                 }catch(e){}
12478             };
12479         }
12480     },
12481
12482     // private
12483     handleResponse : function(o, trans){
12484         this.trans = false;
12485         this.destroyTrans(trans, true);
12486         var result;
12487         try {
12488             result = trans.reader.readRecords(o);
12489         }catch(e){
12490             this.fireEvent("loadexception", this, o, trans.arg, e);
12491             trans.callback.call(trans.scope||window, null, trans.arg, false);
12492             return;
12493         }
12494         this.fireEvent("load", this, o, trans.arg);
12495         trans.callback.call(trans.scope||window, result, trans.arg, true);
12496     },
12497
12498     // private
12499     handleFailure : function(trans){
12500         this.trans = false;
12501         this.destroyTrans(trans, false);
12502         this.fireEvent("loadexception", this, null, trans.arg);
12503         trans.callback.call(trans.scope||window, null, trans.arg, false);
12504     }
12505 });/*
12506  * Based on:
12507  * Ext JS Library 1.1.1
12508  * Copyright(c) 2006-2007, Ext JS, LLC.
12509  *
12510  * Originally Released Under LGPL - original licence link has changed is not relivant.
12511  *
12512  * Fork - LGPL
12513  * <script type="text/javascript">
12514  */
12515
12516 /**
12517  * @class Roo.data.JsonReader
12518  * @extends Roo.data.DataReader
12519  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12520  * based on mappings in a provided Roo.data.Record constructor.
12521  * 
12522  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12523  * in the reply previously. 
12524  * 
12525  * <p>
12526  * Example code:
12527  * <pre><code>
12528 var RecordDef = Roo.data.Record.create([
12529     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12530     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12531 ]);
12532 var myReader = new Roo.data.JsonReader({
12533     totalProperty: "results",    // The property which contains the total dataset size (optional)
12534     root: "rows",                // The property which contains an Array of row objects
12535     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12536 }, RecordDef);
12537 </code></pre>
12538  * <p>
12539  * This would consume a JSON file like this:
12540  * <pre><code>
12541 { 'results': 2, 'rows': [
12542     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12543     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12544 }
12545 </code></pre>
12546  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12547  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12548  * paged from the remote server.
12549  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12550  * @cfg {String} root name of the property which contains the Array of row objects.
12551  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12552  * @cfg {Array} fields Array of field definition objects
12553  * @constructor
12554  * Create a new JsonReader
12555  * @param {Object} meta Metadata configuration options
12556  * @param {Object} recordType Either an Array of field definition objects,
12557  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12558  */
12559 Roo.data.JsonReader = function(meta, recordType){
12560     
12561     meta = meta || {};
12562     // set some defaults:
12563     Roo.applyIf(meta, {
12564         totalProperty: 'total',
12565         successProperty : 'success',
12566         root : 'data',
12567         id : 'id'
12568     });
12569     
12570     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12571 };
12572 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12573     
12574     /**
12575      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12576      * Used by Store query builder to append _requestMeta to params.
12577      * 
12578      */
12579     metaFromRemote : false,
12580     /**
12581      * This method is only used by a DataProxy which has retrieved data from a remote server.
12582      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12583      * @return {Object} data A data block which is used by an Roo.data.Store object as
12584      * a cache of Roo.data.Records.
12585      */
12586     read : function(response){
12587         var json = response.responseText;
12588        
12589         var o = /* eval:var:o */ eval("("+json+")");
12590         if(!o) {
12591             throw {message: "JsonReader.read: Json object not found"};
12592         }
12593         
12594         if(o.metaData){
12595             
12596             delete this.ef;
12597             this.metaFromRemote = true;
12598             this.meta = o.metaData;
12599             this.recordType = Roo.data.Record.create(o.metaData.fields);
12600             this.onMetaChange(this.meta, this.recordType, o);
12601         }
12602         return this.readRecords(o);
12603     },
12604
12605     // private function a store will implement
12606     onMetaChange : function(meta, recordType, o){
12607
12608     },
12609
12610     /**
12611          * @ignore
12612          */
12613     simpleAccess: function(obj, subsc) {
12614         return obj[subsc];
12615     },
12616
12617         /**
12618          * @ignore
12619          */
12620     getJsonAccessor: function(){
12621         var re = /[\[\.]/;
12622         return function(expr) {
12623             try {
12624                 return(re.test(expr))
12625                     ? new Function("obj", "return obj." + expr)
12626                     : function(obj){
12627                         return obj[expr];
12628                     };
12629             } catch(e){}
12630             return Roo.emptyFn;
12631         };
12632     }(),
12633
12634     /**
12635      * Create a data block containing Roo.data.Records from an XML document.
12636      * @param {Object} o An object which contains an Array of row objects in the property specified
12637      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12638      * which contains the total size of the dataset.
12639      * @return {Object} data A data block which is used by an Roo.data.Store object as
12640      * a cache of Roo.data.Records.
12641      */
12642     readRecords : function(o){
12643         /**
12644          * After any data loads, the raw JSON data is available for further custom processing.
12645          * @type Object
12646          */
12647         this.o = o;
12648         var s = this.meta, Record = this.recordType,
12649             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12650
12651 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12652         if (!this.ef) {
12653             if(s.totalProperty) {
12654                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12655                 }
12656                 if(s.successProperty) {
12657                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12658                 }
12659                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12660                 if (s.id) {
12661                         var g = this.getJsonAccessor(s.id);
12662                         this.getId = function(rec) {
12663                                 var r = g(rec);  
12664                                 return (r === undefined || r === "") ? null : r;
12665                         };
12666                 } else {
12667                         this.getId = function(){return null;};
12668                 }
12669             this.ef = [];
12670             for(var jj = 0; jj < fl; jj++){
12671                 f = fi[jj];
12672                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12673                 this.ef[jj] = this.getJsonAccessor(map);
12674             }
12675         }
12676
12677         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12678         if(s.totalProperty){
12679             var vt = parseInt(this.getTotal(o), 10);
12680             if(!isNaN(vt)){
12681                 totalRecords = vt;
12682             }
12683         }
12684         if(s.successProperty){
12685             var vs = this.getSuccess(o);
12686             if(vs === false || vs === 'false'){
12687                 success = false;
12688             }
12689         }
12690         var records = [];
12691         for(var i = 0; i < c; i++){
12692                 var n = root[i];
12693             var values = {};
12694             var id = this.getId(n);
12695             for(var j = 0; j < fl; j++){
12696                 f = fi[j];
12697             var v = this.ef[j](n);
12698             if (!f.convert) {
12699                 Roo.log('missing convert for ' + f.name);
12700                 Roo.log(f);
12701                 continue;
12702             }
12703             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12704             }
12705             var record = new Record(values, id);
12706             record.json = n;
12707             records[i] = record;
12708         }
12709         return {
12710             raw : o,
12711             success : success,
12712             records : records,
12713             totalRecords : totalRecords
12714         };
12715     }
12716 });/*
12717  * Based on:
12718  * Ext JS Library 1.1.1
12719  * Copyright(c) 2006-2007, Ext JS, LLC.
12720  *
12721  * Originally Released Under LGPL - original licence link has changed is not relivant.
12722  *
12723  * Fork - LGPL
12724  * <script type="text/javascript">
12725  */
12726
12727 /**
12728  * @class Roo.data.ArrayReader
12729  * @extends Roo.data.DataReader
12730  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12731  * Each element of that Array represents a row of data fields. The
12732  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12733  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12734  * <p>
12735  * Example code:.
12736  * <pre><code>
12737 var RecordDef = Roo.data.Record.create([
12738     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12739     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12740 ]);
12741 var myReader = new Roo.data.ArrayReader({
12742     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12743 }, RecordDef);
12744 </code></pre>
12745  * <p>
12746  * This would consume an Array like this:
12747  * <pre><code>
12748 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12749   </code></pre>
12750  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12751  * @constructor
12752  * Create a new JsonReader
12753  * @param {Object} meta Metadata configuration options.
12754  * @param {Object} recordType Either an Array of field definition objects
12755  * as specified to {@link Roo.data.Record#create},
12756  * or an {@link Roo.data.Record} object
12757  * created using {@link Roo.data.Record#create}.
12758  */
12759 Roo.data.ArrayReader = function(meta, recordType){
12760     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12761 };
12762
12763 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12764     /**
12765      * Create a data block containing Roo.data.Records from an XML document.
12766      * @param {Object} o An Array of row objects which represents the dataset.
12767      * @return {Object} data A data block which is used by an Roo.data.Store object as
12768      * a cache of Roo.data.Records.
12769      */
12770     readRecords : function(o){
12771         var sid = this.meta ? this.meta.id : null;
12772         var recordType = this.recordType, fields = recordType.prototype.fields;
12773         var records = [];
12774         var root = o;
12775             for(var i = 0; i < root.length; i++){
12776                     var n = root[i];
12777                 var values = {};
12778                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12779                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12780                 var f = fields.items[j];
12781                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12782                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12783                 v = f.convert(v);
12784                 values[f.name] = v;
12785             }
12786                 var record = new recordType(values, id);
12787                 record.json = n;
12788                 records[records.length] = record;
12789             }
12790             return {
12791                 records : records,
12792                 totalRecords : records.length
12793             };
12794     }
12795 });/*
12796  * - LGPL
12797  * * 
12798  */
12799
12800 /**
12801  * @class Roo.bootstrap.ComboBox
12802  * @extends Roo.bootstrap.TriggerField
12803  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12804  * @cfg {Boolean} append (true|false) default false
12805  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12806  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12807  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12808  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12809  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12810  * @cfg {Boolean} animate default true
12811  * @cfg {Boolean} emptyResultText only for touch device
12812  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12813  * @cfg {String} emptyTitle default ''
12814  * @constructor
12815  * Create a new ComboBox.
12816  * @param {Object} config Configuration options
12817  */
12818 Roo.bootstrap.ComboBox = function(config){
12819     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12820     this.addEvents({
12821         /**
12822          * @event expand
12823          * Fires when the dropdown list is expanded
12824         * @param {Roo.bootstrap.ComboBox} combo This combo box
12825         */
12826         'expand' : true,
12827         /**
12828          * @event collapse
12829          * Fires when the dropdown list is collapsed
12830         * @param {Roo.bootstrap.ComboBox} combo This combo box
12831         */
12832         'collapse' : true,
12833         /**
12834          * @event beforeselect
12835          * Fires before a list item is selected. Return false to cancel the selection.
12836         * @param {Roo.bootstrap.ComboBox} combo This combo box
12837         * @param {Roo.data.Record} record The data record returned from the underlying store
12838         * @param {Number} index The index of the selected item in the dropdown list
12839         */
12840         'beforeselect' : true,
12841         /**
12842          * @event select
12843          * Fires when a list item is selected
12844         * @param {Roo.bootstrap.ComboBox} combo This combo box
12845         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12846         * @param {Number} index The index of the selected item in the dropdown list
12847         */
12848         'select' : true,
12849         /**
12850          * @event beforequery
12851          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12852          * The event object passed has these properties:
12853         * @param {Roo.bootstrap.ComboBox} combo This combo box
12854         * @param {String} query The query
12855         * @param {Boolean} forceAll true to force "all" query
12856         * @param {Boolean} cancel true to cancel the query
12857         * @param {Object} e The query event object
12858         */
12859         'beforequery': true,
12860          /**
12861          * @event add
12862          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12863         * @param {Roo.bootstrap.ComboBox} combo This combo box
12864         */
12865         'add' : true,
12866         /**
12867          * @event edit
12868          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12869         * @param {Roo.bootstrap.ComboBox} combo This combo box
12870         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12871         */
12872         'edit' : true,
12873         /**
12874          * @event remove
12875          * Fires when the remove value from the combobox array
12876         * @param {Roo.bootstrap.ComboBox} combo This combo box
12877         */
12878         'remove' : true,
12879         /**
12880          * @event afterremove
12881          * Fires when the remove value from the combobox array
12882         * @param {Roo.bootstrap.ComboBox} combo This combo box
12883         */
12884         'afterremove' : true,
12885         /**
12886          * @event specialfilter
12887          * Fires when specialfilter
12888             * @param {Roo.bootstrap.ComboBox} combo This combo box
12889             */
12890         'specialfilter' : true,
12891         /**
12892          * @event tick
12893          * Fires when tick the element
12894             * @param {Roo.bootstrap.ComboBox} combo This combo box
12895             */
12896         'tick' : true,
12897         /**
12898          * @event touchviewdisplay
12899          * Fires when touch view require special display (default is using displayField)
12900             * @param {Roo.bootstrap.ComboBox} combo This combo box
12901             * @param {Object} cfg set html .
12902             */
12903         'touchviewdisplay' : true
12904         
12905     });
12906     
12907     this.item = [];
12908     this.tickItems = [];
12909     
12910     this.selectedIndex = -1;
12911     if(this.mode == 'local'){
12912         if(config.queryDelay === undefined){
12913             this.queryDelay = 10;
12914         }
12915         if(config.minChars === undefined){
12916             this.minChars = 0;
12917         }
12918     }
12919 };
12920
12921 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12922      
12923     /**
12924      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12925      * rendering into an Roo.Editor, defaults to false)
12926      */
12927     /**
12928      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12929      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12930      */
12931     /**
12932      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12933      */
12934     /**
12935      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12936      * the dropdown list (defaults to undefined, with no header element)
12937      */
12938
12939      /**
12940      * @cfg {String/Roo.Template} tpl The template to use to render the output
12941      */
12942      
12943      /**
12944      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12945      */
12946     listWidth: undefined,
12947     /**
12948      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12949      * mode = 'remote' or 'text' if mode = 'local')
12950      */
12951     displayField: undefined,
12952     
12953     /**
12954      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12955      * mode = 'remote' or 'value' if mode = 'local'). 
12956      * Note: use of a valueField requires the user make a selection
12957      * in order for a value to be mapped.
12958      */
12959     valueField: undefined,
12960     /**
12961      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12962      */
12963     modalTitle : '',
12964     
12965     /**
12966      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12967      * field's data value (defaults to the underlying DOM element's name)
12968      */
12969     hiddenName: undefined,
12970     /**
12971      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12972      */
12973     listClass: '',
12974     /**
12975      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12976      */
12977     selectedClass: 'active',
12978     
12979     /**
12980      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12981      */
12982     shadow:'sides',
12983     /**
12984      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12985      * anchor positions (defaults to 'tl-bl')
12986      */
12987     listAlign: 'tl-bl?',
12988     /**
12989      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12990      */
12991     maxHeight: 300,
12992     /**
12993      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12994      * query specified by the allQuery config option (defaults to 'query')
12995      */
12996     triggerAction: 'query',
12997     /**
12998      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12999      * (defaults to 4, does not apply if editable = false)
13000      */
13001     minChars : 4,
13002     /**
13003      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13004      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13005      */
13006     typeAhead: false,
13007     /**
13008      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13009      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13010      */
13011     queryDelay: 500,
13012     /**
13013      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13014      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13015      */
13016     pageSize: 0,
13017     /**
13018      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13019      * when editable = true (defaults to false)
13020      */
13021     selectOnFocus:false,
13022     /**
13023      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13024      */
13025     queryParam: 'query',
13026     /**
13027      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13028      * when mode = 'remote' (defaults to 'Loading...')
13029      */
13030     loadingText: 'Loading...',
13031     /**
13032      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13033      */
13034     resizable: false,
13035     /**
13036      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13037      */
13038     handleHeight : 8,
13039     /**
13040      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13041      * traditional select (defaults to true)
13042      */
13043     editable: true,
13044     /**
13045      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13046      */
13047     allQuery: '',
13048     /**
13049      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13050      */
13051     mode: 'remote',
13052     /**
13053      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13054      * listWidth has a higher value)
13055      */
13056     minListWidth : 70,
13057     /**
13058      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13059      * allow the user to set arbitrary text into the field (defaults to false)
13060      */
13061     forceSelection:false,
13062     /**
13063      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13064      * if typeAhead = true (defaults to 250)
13065      */
13066     typeAheadDelay : 250,
13067     /**
13068      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13069      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13070      */
13071     valueNotFoundText : undefined,
13072     /**
13073      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13074      */
13075     blockFocus : false,
13076     
13077     /**
13078      * @cfg {Boolean} disableClear Disable showing of clear button.
13079      */
13080     disableClear : false,
13081     /**
13082      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13083      */
13084     alwaysQuery : false,
13085     
13086     /**
13087      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13088      */
13089     multiple : false,
13090     
13091     /**
13092      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13093      */
13094     invalidClass : "has-warning",
13095     
13096     /**
13097      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13098      */
13099     validClass : "has-success",
13100     
13101     /**
13102      * @cfg {Boolean} specialFilter (true|false) special filter default false
13103      */
13104     specialFilter : false,
13105     
13106     /**
13107      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13108      */
13109     mobileTouchView : true,
13110     
13111     /**
13112      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13113      */
13114     useNativeIOS : false,
13115     
13116     /**
13117      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13118      */
13119     mobile_restrict_height : false,
13120     
13121     ios_options : false,
13122     
13123     //private
13124     addicon : false,
13125     editicon: false,
13126     
13127     page: 0,
13128     hasQuery: false,
13129     append: false,
13130     loadNext: false,
13131     autoFocus : true,
13132     tickable : false,
13133     btnPosition : 'right',
13134     triggerList : true,
13135     showToggleBtn : true,
13136     animate : true,
13137     emptyResultText: 'Empty',
13138     triggerText : 'Select',
13139     emptyTitle : '',
13140     
13141     // element that contains real text value.. (when hidden is used..)
13142     
13143     getAutoCreate : function()
13144     {   
13145         var cfg = false;
13146         //render
13147         /*
13148          * Render classic select for iso
13149          */
13150         
13151         if(Roo.isIOS && this.useNativeIOS){
13152             cfg = this.getAutoCreateNativeIOS();
13153             return cfg;
13154         }
13155         
13156         /*
13157          * Touch Devices
13158          */
13159         
13160         if(Roo.isTouch && this.mobileTouchView){
13161             cfg = this.getAutoCreateTouchView();
13162             return cfg;;
13163         }
13164         
13165         /*
13166          *  Normal ComboBox
13167          */
13168         if(!this.tickable){
13169             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13170             return cfg;
13171         }
13172         
13173         /*
13174          *  ComboBox with tickable selections
13175          */
13176              
13177         var align = this.labelAlign || this.parentLabelAlign();
13178         
13179         cfg = {
13180             cls : 'form-group roo-combobox-tickable' //input-group
13181         };
13182         
13183         var btn_text_select = '';
13184         var btn_text_done = '';
13185         var btn_text_cancel = '';
13186         
13187         if (this.btn_text_show) {
13188             btn_text_select = 'Select';
13189             btn_text_done = 'Done';
13190             btn_text_cancel = 'Cancel'; 
13191         }
13192         
13193         var buttons = {
13194             tag : 'div',
13195             cls : 'tickable-buttons',
13196             cn : [
13197                 {
13198                     tag : 'button',
13199                     type : 'button',
13200                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13201                     //html : this.triggerText
13202                     html: btn_text_select
13203                 },
13204                 {
13205                     tag : 'button',
13206                     type : 'button',
13207                     name : 'ok',
13208                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13209                     //html : 'Done'
13210                     html: btn_text_done
13211                 },
13212                 {
13213                     tag : 'button',
13214                     type : 'button',
13215                     name : 'cancel',
13216                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13217                     //html : 'Cancel'
13218                     html: btn_text_cancel
13219                 }
13220             ]
13221         };
13222         
13223         if(this.editable){
13224             buttons.cn.unshift({
13225                 tag: 'input',
13226                 cls: 'roo-select2-search-field-input'
13227             });
13228         }
13229         
13230         var _this = this;
13231         
13232         Roo.each(buttons.cn, function(c){
13233             if (_this.size) {
13234                 c.cls += ' btn-' + _this.size;
13235             }
13236
13237             if (_this.disabled) {
13238                 c.disabled = true;
13239             }
13240         });
13241         
13242         var box = {
13243             tag: 'div',
13244             cn: [
13245                 {
13246                     tag: 'input',
13247                     type : 'hidden',
13248                     cls: 'form-hidden-field'
13249                 },
13250                 {
13251                     tag: 'ul',
13252                     cls: 'roo-select2-choices',
13253                     cn:[
13254                         {
13255                             tag: 'li',
13256                             cls: 'roo-select2-search-field',
13257                             cn: [
13258                                 buttons
13259                             ]
13260                         }
13261                     ]
13262                 }
13263             ]
13264         };
13265         
13266         var combobox = {
13267             cls: 'roo-select2-container input-group roo-select2-container-multi',
13268             cn: [
13269                 box
13270 //                {
13271 //                    tag: 'ul',
13272 //                    cls: 'typeahead typeahead-long dropdown-menu',
13273 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13274 //                }
13275             ]
13276         };
13277         
13278         if(this.hasFeedback && !this.allowBlank){
13279             
13280             var feedback = {
13281                 tag: 'span',
13282                 cls: 'glyphicon form-control-feedback'
13283             };
13284
13285             combobox.cn.push(feedback);
13286         }
13287         
13288         
13289         if (align ==='left' && this.fieldLabel.length) {
13290             
13291             cfg.cls += ' roo-form-group-label-left';
13292             
13293             cfg.cn = [
13294                 {
13295                     tag : 'i',
13296                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13297                     tooltip : 'This field is required'
13298                 },
13299                 {
13300                     tag: 'label',
13301                     'for' :  id,
13302                     cls : 'control-label',
13303                     html : this.fieldLabel
13304
13305                 },
13306                 {
13307                     cls : "", 
13308                     cn: [
13309                         combobox
13310                     ]
13311                 }
13312
13313             ];
13314             
13315             var labelCfg = cfg.cn[1];
13316             var contentCfg = cfg.cn[2];
13317             
13318
13319             if(this.indicatorpos == 'right'){
13320                 
13321                 cfg.cn = [
13322                     {
13323                         tag: 'label',
13324                         'for' :  id,
13325                         cls : 'control-label',
13326                         cn : [
13327                             {
13328                                 tag : 'span',
13329                                 html : this.fieldLabel
13330                             },
13331                             {
13332                                 tag : 'i',
13333                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13334                                 tooltip : 'This field is required'
13335                             }
13336                         ]
13337                     },
13338                     {
13339                         cls : "",
13340                         cn: [
13341                             combobox
13342                         ]
13343                     }
13344
13345                 ];
13346                 
13347                 
13348                 
13349                 labelCfg = cfg.cn[0];
13350                 contentCfg = cfg.cn[1];
13351             
13352             }
13353             
13354             if(this.labelWidth > 12){
13355                 labelCfg.style = "width: " + this.labelWidth + 'px';
13356             }
13357             
13358             if(this.labelWidth < 13 && this.labelmd == 0){
13359                 this.labelmd = this.labelWidth;
13360             }
13361             
13362             if(this.labellg > 0){
13363                 labelCfg.cls += ' col-lg-' + this.labellg;
13364                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13365             }
13366             
13367             if(this.labelmd > 0){
13368                 labelCfg.cls += ' col-md-' + this.labelmd;
13369                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13370             }
13371             
13372             if(this.labelsm > 0){
13373                 labelCfg.cls += ' col-sm-' + this.labelsm;
13374                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13375             }
13376             
13377             if(this.labelxs > 0){
13378                 labelCfg.cls += ' col-xs-' + this.labelxs;
13379                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13380             }
13381                 
13382                 
13383         } else if ( this.fieldLabel.length) {
13384 //                Roo.log(" label");
13385                  cfg.cn = [
13386                     {
13387                         tag : 'i',
13388                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13389                         tooltip : 'This field is required'
13390                     },
13391                     {
13392                         tag: 'label',
13393                         //cls : 'input-group-addon',
13394                         html : this.fieldLabel
13395                     },
13396                     combobox
13397                 ];
13398                 
13399                 if(this.indicatorpos == 'right'){
13400                     cfg.cn = [
13401                         {
13402                             tag: 'label',
13403                             //cls : 'input-group-addon',
13404                             html : this.fieldLabel
13405                         },
13406                         {
13407                             tag : 'i',
13408                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13409                             tooltip : 'This field is required'
13410                         },
13411                         combobox
13412                     ];
13413                     
13414                 }
13415
13416         } else {
13417             
13418 //                Roo.log(" no label && no align");
13419                 cfg = combobox
13420                      
13421                 
13422         }
13423          
13424         var settings=this;
13425         ['xs','sm','md','lg'].map(function(size){
13426             if (settings[size]) {
13427                 cfg.cls += ' col-' + size + '-' + settings[size];
13428             }
13429         });
13430         
13431         return cfg;
13432         
13433     },
13434     
13435     _initEventsCalled : false,
13436     
13437     // private
13438     initEvents: function()
13439     {   
13440         if (this._initEventsCalled) { // as we call render... prevent looping...
13441             return;
13442         }
13443         this._initEventsCalled = true;
13444         
13445         if (!this.store) {
13446             throw "can not find store for combo";
13447         }
13448         
13449         this.indicator = this.indicatorEl();
13450         
13451         this.store = Roo.factory(this.store, Roo.data);
13452         this.store.parent = this;
13453         
13454         // if we are building from html. then this element is so complex, that we can not really
13455         // use the rendered HTML.
13456         // so we have to trash and replace the previous code.
13457         if (Roo.XComponent.build_from_html) {
13458             // remove this element....
13459             var e = this.el.dom, k=0;
13460             while (e ) { e = e.previousSibling;  ++k;}
13461
13462             this.el.remove();
13463             
13464             this.el=false;
13465             this.rendered = false;
13466             
13467             this.render(this.parent().getChildContainer(true), k);
13468         }
13469         
13470         if(Roo.isIOS && this.useNativeIOS){
13471             this.initIOSView();
13472             return;
13473         }
13474         
13475         /*
13476          * Touch Devices
13477          */
13478         
13479         if(Roo.isTouch && this.mobileTouchView){
13480             this.initTouchView();
13481             return;
13482         }
13483         
13484         if(this.tickable){
13485             this.initTickableEvents();
13486             return;
13487         }
13488         
13489         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13490         
13491         if(this.hiddenName){
13492             
13493             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13494             
13495             this.hiddenField.dom.value =
13496                 this.hiddenValue !== undefined ? this.hiddenValue :
13497                 this.value !== undefined ? this.value : '';
13498
13499             // prevent input submission
13500             this.el.dom.removeAttribute('name');
13501             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13502              
13503              
13504         }
13505         //if(Roo.isGecko){
13506         //    this.el.dom.setAttribute('autocomplete', 'off');
13507         //}
13508         
13509         var cls = 'x-combo-list';
13510         
13511         //this.list = new Roo.Layer({
13512         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13513         //});
13514         
13515         var _this = this;
13516         
13517         (function(){
13518             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13519             _this.list.setWidth(lw);
13520         }).defer(100);
13521         
13522         this.list.on('mouseover', this.onViewOver, this);
13523         this.list.on('mousemove', this.onViewMove, this);
13524         this.list.on('scroll', this.onViewScroll, this);
13525         
13526         /*
13527         this.list.swallowEvent('mousewheel');
13528         this.assetHeight = 0;
13529
13530         if(this.title){
13531             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13532             this.assetHeight += this.header.getHeight();
13533         }
13534
13535         this.innerList = this.list.createChild({cls:cls+'-inner'});
13536         this.innerList.on('mouseover', this.onViewOver, this);
13537         this.innerList.on('mousemove', this.onViewMove, this);
13538         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13539         
13540         if(this.allowBlank && !this.pageSize && !this.disableClear){
13541             this.footer = this.list.createChild({cls:cls+'-ft'});
13542             this.pageTb = new Roo.Toolbar(this.footer);
13543            
13544         }
13545         if(this.pageSize){
13546             this.footer = this.list.createChild({cls:cls+'-ft'});
13547             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13548                     {pageSize: this.pageSize});
13549             
13550         }
13551         
13552         if (this.pageTb && this.allowBlank && !this.disableClear) {
13553             var _this = this;
13554             this.pageTb.add(new Roo.Toolbar.Fill(), {
13555                 cls: 'x-btn-icon x-btn-clear',
13556                 text: '&#160;',
13557                 handler: function()
13558                 {
13559                     _this.collapse();
13560                     _this.clearValue();
13561                     _this.onSelect(false, -1);
13562                 }
13563             });
13564         }
13565         if (this.footer) {
13566             this.assetHeight += this.footer.getHeight();
13567         }
13568         */
13569             
13570         if(!this.tpl){
13571             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13572         }
13573
13574         this.view = new Roo.View(this.list, this.tpl, {
13575             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13576         });
13577         //this.view.wrapEl.setDisplayed(false);
13578         this.view.on('click', this.onViewClick, this);
13579         
13580         
13581         this.store.on('beforeload', this.onBeforeLoad, this);
13582         this.store.on('load', this.onLoad, this);
13583         this.store.on('loadexception', this.onLoadException, this);
13584         /*
13585         if(this.resizable){
13586             this.resizer = new Roo.Resizable(this.list,  {
13587                pinned:true, handles:'se'
13588             });
13589             this.resizer.on('resize', function(r, w, h){
13590                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13591                 this.listWidth = w;
13592                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13593                 this.restrictHeight();
13594             }, this);
13595             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13596         }
13597         */
13598         if(!this.editable){
13599             this.editable = true;
13600             this.setEditable(false);
13601         }
13602         
13603         /*
13604         
13605         if (typeof(this.events.add.listeners) != 'undefined') {
13606             
13607             this.addicon = this.wrap.createChild(
13608                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13609        
13610             this.addicon.on('click', function(e) {
13611                 this.fireEvent('add', this);
13612             }, this);
13613         }
13614         if (typeof(this.events.edit.listeners) != 'undefined') {
13615             
13616             this.editicon = this.wrap.createChild(
13617                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13618             if (this.addicon) {
13619                 this.editicon.setStyle('margin-left', '40px');
13620             }
13621             this.editicon.on('click', function(e) {
13622                 
13623                 // we fire even  if inothing is selected..
13624                 this.fireEvent('edit', this, this.lastData );
13625                 
13626             }, this);
13627         }
13628         */
13629         
13630         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13631             "up" : function(e){
13632                 this.inKeyMode = true;
13633                 this.selectPrev();
13634             },
13635
13636             "down" : function(e){
13637                 if(!this.isExpanded()){
13638                     this.onTriggerClick();
13639                 }else{
13640                     this.inKeyMode = true;
13641                     this.selectNext();
13642                 }
13643             },
13644
13645             "enter" : function(e){
13646 //                this.onViewClick();
13647                 //return true;
13648                 this.collapse();
13649                 
13650                 if(this.fireEvent("specialkey", this, e)){
13651                     this.onViewClick(false);
13652                 }
13653                 
13654                 return true;
13655             },
13656
13657             "esc" : function(e){
13658                 this.collapse();
13659             },
13660
13661             "tab" : function(e){
13662                 this.collapse();
13663                 
13664                 if(this.fireEvent("specialkey", this, e)){
13665                     this.onViewClick(false);
13666                 }
13667                 
13668                 return true;
13669             },
13670
13671             scope : this,
13672
13673             doRelay : function(foo, bar, hname){
13674                 if(hname == 'down' || this.scope.isExpanded()){
13675                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13676                 }
13677                 return true;
13678             },
13679
13680             forceKeyDown: true
13681         });
13682         
13683         
13684         this.queryDelay = Math.max(this.queryDelay || 10,
13685                 this.mode == 'local' ? 10 : 250);
13686         
13687         
13688         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13689         
13690         if(this.typeAhead){
13691             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13692         }
13693         if(this.editable !== false){
13694             this.inputEl().on("keyup", this.onKeyUp, this);
13695         }
13696         if(this.forceSelection){
13697             this.inputEl().on('blur', this.doForce, this);
13698         }
13699         
13700         if(this.multiple){
13701             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13702             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13703         }
13704     },
13705     
13706     initTickableEvents: function()
13707     {   
13708         this.createList();
13709         
13710         if(this.hiddenName){
13711             
13712             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13713             
13714             this.hiddenField.dom.value =
13715                 this.hiddenValue !== undefined ? this.hiddenValue :
13716                 this.value !== undefined ? this.value : '';
13717
13718             // prevent input submission
13719             this.el.dom.removeAttribute('name');
13720             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13721              
13722              
13723         }
13724         
13725 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13726         
13727         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13728         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13729         if(this.triggerList){
13730             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13731         }
13732          
13733         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13734         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13735         
13736         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13737         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13738         
13739         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13740         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13741         
13742         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13743         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13744         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13745         
13746         this.okBtn.hide();
13747         this.cancelBtn.hide();
13748         
13749         var _this = this;
13750         
13751         (function(){
13752             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13753             _this.list.setWidth(lw);
13754         }).defer(100);
13755         
13756         this.list.on('mouseover', this.onViewOver, this);
13757         this.list.on('mousemove', this.onViewMove, this);
13758         
13759         this.list.on('scroll', this.onViewScroll, this);
13760         
13761         if(!this.tpl){
13762             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13763                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13764         }
13765
13766         this.view = new Roo.View(this.list, this.tpl, {
13767             singleSelect:true,
13768             tickable:true,
13769             parent:this,
13770             store: this.store,
13771             selectedClass: this.selectedClass
13772         });
13773         
13774         //this.view.wrapEl.setDisplayed(false);
13775         this.view.on('click', this.onViewClick, this);
13776         
13777         
13778         
13779         this.store.on('beforeload', this.onBeforeLoad, this);
13780         this.store.on('load', this.onLoad, this);
13781         this.store.on('loadexception', this.onLoadException, this);
13782         
13783         if(this.editable){
13784             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13785                 "up" : function(e){
13786                     this.inKeyMode = true;
13787                     this.selectPrev();
13788                 },
13789
13790                 "down" : function(e){
13791                     this.inKeyMode = true;
13792                     this.selectNext();
13793                 },
13794
13795                 "enter" : function(e){
13796                     if(this.fireEvent("specialkey", this, e)){
13797                         this.onViewClick(false);
13798                     }
13799                     
13800                     return true;
13801                 },
13802
13803                 "esc" : function(e){
13804                     this.onTickableFooterButtonClick(e, false, false);
13805                 },
13806
13807                 "tab" : function(e){
13808                     this.fireEvent("specialkey", this, e);
13809                     
13810                     this.onTickableFooterButtonClick(e, false, false);
13811                     
13812                     return true;
13813                 },
13814
13815                 scope : this,
13816
13817                 doRelay : function(e, fn, key){
13818                     if(this.scope.isExpanded()){
13819                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13820                     }
13821                     return true;
13822                 },
13823
13824                 forceKeyDown: true
13825             });
13826         }
13827         
13828         this.queryDelay = Math.max(this.queryDelay || 10,
13829                 this.mode == 'local' ? 10 : 250);
13830         
13831         
13832         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13833         
13834         if(this.typeAhead){
13835             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13836         }
13837         
13838         if(this.editable !== false){
13839             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13840         }
13841         
13842         this.indicator = this.indicatorEl();
13843         
13844         if(this.indicator){
13845             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13846             this.indicator.hide();
13847         }
13848         
13849     },
13850
13851     onDestroy : function(){
13852         if(this.view){
13853             this.view.setStore(null);
13854             this.view.el.removeAllListeners();
13855             this.view.el.remove();
13856             this.view.purgeListeners();
13857         }
13858         if(this.list){
13859             this.list.dom.innerHTML  = '';
13860         }
13861         
13862         if(this.store){
13863             this.store.un('beforeload', this.onBeforeLoad, this);
13864             this.store.un('load', this.onLoad, this);
13865             this.store.un('loadexception', this.onLoadException, this);
13866         }
13867         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13868     },
13869
13870     // private
13871     fireKey : function(e){
13872         if(e.isNavKeyPress() && !this.list.isVisible()){
13873             this.fireEvent("specialkey", this, e);
13874         }
13875     },
13876
13877     // private
13878     onResize: function(w, h){
13879 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13880 //        
13881 //        if(typeof w != 'number'){
13882 //            // we do not handle it!?!?
13883 //            return;
13884 //        }
13885 //        var tw = this.trigger.getWidth();
13886 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13887 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13888 //        var x = w - tw;
13889 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13890 //            
13891 //        //this.trigger.setStyle('left', x+'px');
13892 //        
13893 //        if(this.list && this.listWidth === undefined){
13894 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13895 //            this.list.setWidth(lw);
13896 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13897 //        }
13898         
13899     
13900         
13901     },
13902
13903     /**
13904      * Allow or prevent the user from directly editing the field text.  If false is passed,
13905      * the user will only be able to select from the items defined in the dropdown list.  This method
13906      * is the runtime equivalent of setting the 'editable' config option at config time.
13907      * @param {Boolean} value True to allow the user to directly edit the field text
13908      */
13909     setEditable : function(value){
13910         if(value == this.editable){
13911             return;
13912         }
13913         this.editable = value;
13914         if(!value){
13915             this.inputEl().dom.setAttribute('readOnly', true);
13916             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13917             this.inputEl().addClass('x-combo-noedit');
13918         }else{
13919             this.inputEl().dom.setAttribute('readOnly', false);
13920             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13921             this.inputEl().removeClass('x-combo-noedit');
13922         }
13923     },
13924
13925     // private
13926     
13927     onBeforeLoad : function(combo,opts){
13928         if(!this.hasFocus){
13929             return;
13930         }
13931          if (!opts.add) {
13932             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13933          }
13934         this.restrictHeight();
13935         this.selectedIndex = -1;
13936     },
13937
13938     // private
13939     onLoad : function(){
13940         
13941         this.hasQuery = false;
13942         
13943         if(!this.hasFocus){
13944             return;
13945         }
13946         
13947         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13948             this.loading.hide();
13949         }
13950         
13951         if(this.store.getCount() > 0){
13952             
13953             this.expand();
13954             this.restrictHeight();
13955             if(this.lastQuery == this.allQuery){
13956                 if(this.editable && !this.tickable){
13957                     this.inputEl().dom.select();
13958                 }
13959                 
13960                 if(
13961                     !this.selectByValue(this.value, true) &&
13962                     this.autoFocus && 
13963                     (
13964                         !this.store.lastOptions ||
13965                         typeof(this.store.lastOptions.add) == 'undefined' || 
13966                         this.store.lastOptions.add != true
13967                     )
13968                 ){
13969                     this.select(0, true);
13970                 }
13971             }else{
13972                 if(this.autoFocus){
13973                     this.selectNext();
13974                 }
13975                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13976                     this.taTask.delay(this.typeAheadDelay);
13977                 }
13978             }
13979         }else{
13980             this.onEmptyResults();
13981         }
13982         
13983         //this.el.focus();
13984     },
13985     // private
13986     onLoadException : function()
13987     {
13988         this.hasQuery = false;
13989         
13990         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13991             this.loading.hide();
13992         }
13993         
13994         if(this.tickable && this.editable){
13995             return;
13996         }
13997         
13998         this.collapse();
13999         // only causes errors at present
14000         //Roo.log(this.store.reader.jsonData);
14001         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14002             // fixme
14003             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14004         //}
14005         
14006         
14007     },
14008     // private
14009     onTypeAhead : function(){
14010         if(this.store.getCount() > 0){
14011             var r = this.store.getAt(0);
14012             var newValue = r.data[this.displayField];
14013             var len = newValue.length;
14014             var selStart = this.getRawValue().length;
14015             
14016             if(selStart != len){
14017                 this.setRawValue(newValue);
14018                 this.selectText(selStart, newValue.length);
14019             }
14020         }
14021     },
14022
14023     // private
14024     onSelect : function(record, index){
14025         
14026         if(this.fireEvent('beforeselect', this, record, index) !== false){
14027         
14028             this.setFromData(index > -1 ? record.data : false);
14029             
14030             this.collapse();
14031             this.fireEvent('select', this, record, index);
14032         }
14033     },
14034
14035     /**
14036      * Returns the currently selected field value or empty string if no value is set.
14037      * @return {String} value The selected value
14038      */
14039     getValue : function()
14040     {
14041         if(Roo.isIOS && this.useNativeIOS){
14042             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14043         }
14044         
14045         if(this.multiple){
14046             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14047         }
14048         
14049         if(this.valueField){
14050             return typeof this.value != 'undefined' ? this.value : '';
14051         }else{
14052             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14053         }
14054     },
14055     
14056     getRawValue : function()
14057     {
14058         if(Roo.isIOS && this.useNativeIOS){
14059             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14060         }
14061         
14062         var v = this.inputEl().getValue();
14063         
14064         return v;
14065     },
14066
14067     /**
14068      * Clears any text/value currently set in the field
14069      */
14070     clearValue : function(){
14071         
14072         if(this.hiddenField){
14073             this.hiddenField.dom.value = '';
14074         }
14075         this.value = '';
14076         this.setRawValue('');
14077         this.lastSelectionText = '';
14078         this.lastData = false;
14079         
14080         var close = this.closeTriggerEl();
14081         
14082         if(close){
14083             close.hide();
14084         }
14085         
14086         this.validate();
14087         
14088     },
14089
14090     /**
14091      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14092      * will be displayed in the field.  If the value does not match the data value of an existing item,
14093      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14094      * Otherwise the field will be blank (although the value will still be set).
14095      * @param {String} value The value to match
14096      */
14097     setValue : function(v)
14098     {
14099         if(Roo.isIOS && this.useNativeIOS){
14100             this.setIOSValue(v);
14101             return;
14102         }
14103         
14104         if(this.multiple){
14105             this.syncValue();
14106             return;
14107         }
14108         
14109         var text = v;
14110         if(this.valueField){
14111             var r = this.findRecord(this.valueField, v);
14112             if(r){
14113                 text = r.data[this.displayField];
14114             }else if(this.valueNotFoundText !== undefined){
14115                 text = this.valueNotFoundText;
14116             }
14117         }
14118         this.lastSelectionText = text;
14119         if(this.hiddenField){
14120             this.hiddenField.dom.value = v;
14121         }
14122         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14123         this.value = v;
14124         
14125         var close = this.closeTriggerEl();
14126         
14127         if(close){
14128             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14129         }
14130         
14131         this.validate();
14132     },
14133     /**
14134      * @property {Object} the last set data for the element
14135      */
14136     
14137     lastData : false,
14138     /**
14139      * Sets the value of the field based on a object which is related to the record format for the store.
14140      * @param {Object} value the value to set as. or false on reset?
14141      */
14142     setFromData : function(o){
14143         
14144         if(this.multiple){
14145             this.addItem(o);
14146             return;
14147         }
14148             
14149         var dv = ''; // display value
14150         var vv = ''; // value value..
14151         this.lastData = o;
14152         if (this.displayField) {
14153             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14154         } else {
14155             // this is an error condition!!!
14156             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14157         }
14158         
14159         if(this.valueField){
14160             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14161         }
14162         
14163         var close = this.closeTriggerEl();
14164         
14165         if(close){
14166             if(dv.length || vv * 1 > 0){
14167                 close.show() ;
14168                 this.blockFocus=true;
14169             } else {
14170                 close.hide();
14171             }             
14172         }
14173         
14174         if(this.hiddenField){
14175             this.hiddenField.dom.value = vv;
14176             
14177             this.lastSelectionText = dv;
14178             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14179             this.value = vv;
14180             return;
14181         }
14182         // no hidden field.. - we store the value in 'value', but still display
14183         // display field!!!!
14184         this.lastSelectionText = dv;
14185         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14186         this.value = vv;
14187         
14188         
14189         
14190     },
14191     // private
14192     reset : function(){
14193         // overridden so that last data is reset..
14194         
14195         if(this.multiple){
14196             this.clearItem();
14197             return;
14198         }
14199         
14200         this.setValue(this.originalValue);
14201         //this.clearInvalid();
14202         this.lastData = false;
14203         if (this.view) {
14204             this.view.clearSelections();
14205         }
14206         
14207         this.validate();
14208     },
14209     // private
14210     findRecord : function(prop, value){
14211         var record;
14212         if(this.store.getCount() > 0){
14213             this.store.each(function(r){
14214                 if(r.data[prop] == value){
14215                     record = r;
14216                     return false;
14217                 }
14218                 return true;
14219             });
14220         }
14221         return record;
14222     },
14223     
14224     getName: function()
14225     {
14226         // returns hidden if it's set..
14227         if (!this.rendered) {return ''};
14228         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14229         
14230     },
14231     // private
14232     onViewMove : function(e, t){
14233         this.inKeyMode = false;
14234     },
14235
14236     // private
14237     onViewOver : function(e, t){
14238         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14239             return;
14240         }
14241         var item = this.view.findItemFromChild(t);
14242         
14243         if(item){
14244             var index = this.view.indexOf(item);
14245             this.select(index, false);
14246         }
14247     },
14248
14249     // private
14250     onViewClick : function(view, doFocus, el, e)
14251     {
14252         var index = this.view.getSelectedIndexes()[0];
14253         
14254         var r = this.store.getAt(index);
14255         
14256         if(this.tickable){
14257             
14258             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14259                 return;
14260             }
14261             
14262             var rm = false;
14263             var _this = this;
14264             
14265             Roo.each(this.tickItems, function(v,k){
14266                 
14267                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14268                     Roo.log(v);
14269                     _this.tickItems.splice(k, 1);
14270                     
14271                     if(typeof(e) == 'undefined' && view == false){
14272                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14273                     }
14274                     
14275                     rm = true;
14276                     return;
14277                 }
14278             });
14279             
14280             if(rm){
14281                 return;
14282             }
14283             
14284             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14285                 this.tickItems.push(r.data);
14286             }
14287             
14288             if(typeof(e) == 'undefined' && view == false){
14289                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14290             }
14291                     
14292             return;
14293         }
14294         
14295         if(r){
14296             this.onSelect(r, index);
14297         }
14298         if(doFocus !== false && !this.blockFocus){
14299             this.inputEl().focus();
14300         }
14301     },
14302
14303     // private
14304     restrictHeight : function(){
14305         //this.innerList.dom.style.height = '';
14306         //var inner = this.innerList.dom;
14307         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14308         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14309         //this.list.beginUpdate();
14310         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14311         this.list.alignTo(this.inputEl(), this.listAlign);
14312         this.list.alignTo(this.inputEl(), this.listAlign);
14313         //this.list.endUpdate();
14314     },
14315
14316     // private
14317     onEmptyResults : function(){
14318         
14319         if(this.tickable && this.editable){
14320             this.hasFocus = false;
14321             this.restrictHeight();
14322             return;
14323         }
14324         
14325         this.collapse();
14326     },
14327
14328     /**
14329      * Returns true if the dropdown list is expanded, else false.
14330      */
14331     isExpanded : function(){
14332         return this.list.isVisible();
14333     },
14334
14335     /**
14336      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14337      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14338      * @param {String} value The data value of the item to select
14339      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14340      * selected item if it is not currently in view (defaults to true)
14341      * @return {Boolean} True if the value matched an item in the list, else false
14342      */
14343     selectByValue : function(v, scrollIntoView){
14344         if(v !== undefined && v !== null){
14345             var r = this.findRecord(this.valueField || this.displayField, v);
14346             if(r){
14347                 this.select(this.store.indexOf(r), scrollIntoView);
14348                 return true;
14349             }
14350         }
14351         return false;
14352     },
14353
14354     /**
14355      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14356      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14357      * @param {Number} index The zero-based index of the list item to select
14358      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14359      * selected item if it is not currently in view (defaults to true)
14360      */
14361     select : function(index, scrollIntoView){
14362         this.selectedIndex = index;
14363         this.view.select(index);
14364         if(scrollIntoView !== false){
14365             var el = this.view.getNode(index);
14366             /*
14367              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14368              */
14369             if(el){
14370                 this.list.scrollChildIntoView(el, false);
14371             }
14372         }
14373     },
14374
14375     // private
14376     selectNext : function(){
14377         var ct = this.store.getCount();
14378         if(ct > 0){
14379             if(this.selectedIndex == -1){
14380                 this.select(0);
14381             }else if(this.selectedIndex < ct-1){
14382                 this.select(this.selectedIndex+1);
14383             }
14384         }
14385     },
14386
14387     // private
14388     selectPrev : function(){
14389         var ct = this.store.getCount();
14390         if(ct > 0){
14391             if(this.selectedIndex == -1){
14392                 this.select(0);
14393             }else if(this.selectedIndex != 0){
14394                 this.select(this.selectedIndex-1);
14395             }
14396         }
14397     },
14398
14399     // private
14400     onKeyUp : function(e){
14401         if(this.editable !== false && !e.isSpecialKey()){
14402             this.lastKey = e.getKey();
14403             this.dqTask.delay(this.queryDelay);
14404         }
14405     },
14406
14407     // private
14408     validateBlur : function(){
14409         return !this.list || !this.list.isVisible();   
14410     },
14411
14412     // private
14413     initQuery : function(){
14414         
14415         var v = this.getRawValue();
14416         
14417         if(this.tickable && this.editable){
14418             v = this.tickableInputEl().getValue();
14419         }
14420         
14421         this.doQuery(v);
14422     },
14423
14424     // private
14425     doForce : function(){
14426         if(this.inputEl().dom.value.length > 0){
14427             this.inputEl().dom.value =
14428                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14429              
14430         }
14431     },
14432
14433     /**
14434      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14435      * query allowing the query action to be canceled if needed.
14436      * @param {String} query The SQL query to execute
14437      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14438      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14439      * saved in the current store (defaults to false)
14440      */
14441     doQuery : function(q, forceAll){
14442         
14443         if(q === undefined || q === null){
14444             q = '';
14445         }
14446         var qe = {
14447             query: q,
14448             forceAll: forceAll,
14449             combo: this,
14450             cancel:false
14451         };
14452         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14453             return false;
14454         }
14455         q = qe.query;
14456         
14457         forceAll = qe.forceAll;
14458         if(forceAll === true || (q.length >= this.minChars)){
14459             
14460             this.hasQuery = true;
14461             
14462             if(this.lastQuery != q || this.alwaysQuery){
14463                 this.lastQuery = q;
14464                 if(this.mode == 'local'){
14465                     this.selectedIndex = -1;
14466                     if(forceAll){
14467                         this.store.clearFilter();
14468                     }else{
14469                         
14470                         if(this.specialFilter){
14471                             this.fireEvent('specialfilter', this);
14472                             this.onLoad();
14473                             return;
14474                         }
14475                         
14476                         this.store.filter(this.displayField, q);
14477                     }
14478                     
14479                     this.store.fireEvent("datachanged", this.store);
14480                     
14481                     this.onLoad();
14482                     
14483                     
14484                 }else{
14485                     
14486                     this.store.baseParams[this.queryParam] = q;
14487                     
14488                     var options = {params : this.getParams(q)};
14489                     
14490                     if(this.loadNext){
14491                         options.add = true;
14492                         options.params.start = this.page * this.pageSize;
14493                     }
14494                     
14495                     this.store.load(options);
14496                     
14497                     /*
14498                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14499                      *  we should expand the list on onLoad
14500                      *  so command out it
14501                      */
14502 //                    this.expand();
14503                 }
14504             }else{
14505                 this.selectedIndex = -1;
14506                 this.onLoad();   
14507             }
14508         }
14509         
14510         this.loadNext = false;
14511     },
14512     
14513     // private
14514     getParams : function(q){
14515         var p = {};
14516         //p[this.queryParam] = q;
14517         
14518         if(this.pageSize){
14519             p.start = 0;
14520             p.limit = this.pageSize;
14521         }
14522         return p;
14523     },
14524
14525     /**
14526      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14527      */
14528     collapse : function(){
14529         if(!this.isExpanded()){
14530             return;
14531         }
14532         
14533         this.list.hide();
14534         
14535         this.hasFocus = false;
14536         
14537         if(this.tickable){
14538             this.okBtn.hide();
14539             this.cancelBtn.hide();
14540             this.trigger.show();
14541             
14542             if(this.editable){
14543                 this.tickableInputEl().dom.value = '';
14544                 this.tickableInputEl().blur();
14545             }
14546             
14547         }
14548         
14549         Roo.get(document).un('mousedown', this.collapseIf, this);
14550         Roo.get(document).un('mousewheel', this.collapseIf, this);
14551         if (!this.editable) {
14552             Roo.get(document).un('keydown', this.listKeyPress, this);
14553         }
14554         this.fireEvent('collapse', this);
14555         
14556         this.validate();
14557     },
14558
14559     // private
14560     collapseIf : function(e){
14561         var in_combo  = e.within(this.el);
14562         var in_list =  e.within(this.list);
14563         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14564         
14565         if (in_combo || in_list || is_list) {
14566             //e.stopPropagation();
14567             return;
14568         }
14569         
14570         if(this.tickable){
14571             this.onTickableFooterButtonClick(e, false, false);
14572         }
14573
14574         this.collapse();
14575         
14576     },
14577
14578     /**
14579      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14580      */
14581     expand : function(){
14582        
14583         if(this.isExpanded() || !this.hasFocus){
14584             return;
14585         }
14586         
14587         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14588         this.list.setWidth(lw);
14589         
14590         Roo.log('expand');
14591         
14592         this.list.show();
14593         
14594         this.restrictHeight();
14595         
14596         if(this.tickable){
14597             
14598             this.tickItems = Roo.apply([], this.item);
14599             
14600             this.okBtn.show();
14601             this.cancelBtn.show();
14602             this.trigger.hide();
14603             
14604             if(this.editable){
14605                 this.tickableInputEl().focus();
14606             }
14607             
14608         }
14609         
14610         Roo.get(document).on('mousedown', this.collapseIf, this);
14611         Roo.get(document).on('mousewheel', this.collapseIf, this);
14612         if (!this.editable) {
14613             Roo.get(document).on('keydown', this.listKeyPress, this);
14614         }
14615         
14616         this.fireEvent('expand', this);
14617     },
14618
14619     // private
14620     // Implements the default empty TriggerField.onTriggerClick function
14621     onTriggerClick : function(e)
14622     {
14623         Roo.log('trigger click');
14624         
14625         if(this.disabled || !this.triggerList){
14626             return;
14627         }
14628         
14629         this.page = 0;
14630         this.loadNext = false;
14631         
14632         if(this.isExpanded()){
14633             this.collapse();
14634             if (!this.blockFocus) {
14635                 this.inputEl().focus();
14636             }
14637             
14638         }else {
14639             this.hasFocus = true;
14640             if(this.triggerAction == 'all') {
14641                 this.doQuery(this.allQuery, true);
14642             } else {
14643                 this.doQuery(this.getRawValue());
14644             }
14645             if (!this.blockFocus) {
14646                 this.inputEl().focus();
14647             }
14648         }
14649     },
14650     
14651     onTickableTriggerClick : function(e)
14652     {
14653         if(this.disabled){
14654             return;
14655         }
14656         
14657         this.page = 0;
14658         this.loadNext = false;
14659         this.hasFocus = true;
14660         
14661         if(this.triggerAction == 'all') {
14662             this.doQuery(this.allQuery, true);
14663         } else {
14664             this.doQuery(this.getRawValue());
14665         }
14666     },
14667     
14668     onSearchFieldClick : function(e)
14669     {
14670         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14671             this.onTickableFooterButtonClick(e, false, false);
14672             return;
14673         }
14674         
14675         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14676             return;
14677         }
14678         
14679         this.page = 0;
14680         this.loadNext = false;
14681         this.hasFocus = true;
14682         
14683         if(this.triggerAction == 'all') {
14684             this.doQuery(this.allQuery, true);
14685         } else {
14686             this.doQuery(this.getRawValue());
14687         }
14688     },
14689     
14690     listKeyPress : function(e)
14691     {
14692         //Roo.log('listkeypress');
14693         // scroll to first matching element based on key pres..
14694         if (e.isSpecialKey()) {
14695             return false;
14696         }
14697         var k = String.fromCharCode(e.getKey()).toUpperCase();
14698         //Roo.log(k);
14699         var match  = false;
14700         var csel = this.view.getSelectedNodes();
14701         var cselitem = false;
14702         if (csel.length) {
14703             var ix = this.view.indexOf(csel[0]);
14704             cselitem  = this.store.getAt(ix);
14705             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14706                 cselitem = false;
14707             }
14708             
14709         }
14710         
14711         this.store.each(function(v) { 
14712             if (cselitem) {
14713                 // start at existing selection.
14714                 if (cselitem.id == v.id) {
14715                     cselitem = false;
14716                 }
14717                 return true;
14718             }
14719                 
14720             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14721                 match = this.store.indexOf(v);
14722                 return false;
14723             }
14724             return true;
14725         }, this);
14726         
14727         if (match === false) {
14728             return true; // no more action?
14729         }
14730         // scroll to?
14731         this.view.select(match);
14732         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14733         sn.scrollIntoView(sn.dom.parentNode, false);
14734     },
14735     
14736     onViewScroll : function(e, t){
14737         
14738         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){
14739             return;
14740         }
14741         
14742         this.hasQuery = true;
14743         
14744         this.loading = this.list.select('.loading', true).first();
14745         
14746         if(this.loading === null){
14747             this.list.createChild({
14748                 tag: 'div',
14749                 cls: 'loading roo-select2-more-results roo-select2-active',
14750                 html: 'Loading more results...'
14751             });
14752             
14753             this.loading = this.list.select('.loading', true).first();
14754             
14755             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14756             
14757             this.loading.hide();
14758         }
14759         
14760         this.loading.show();
14761         
14762         var _combo = this;
14763         
14764         this.page++;
14765         this.loadNext = true;
14766         
14767         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14768         
14769         return;
14770     },
14771     
14772     addItem : function(o)
14773     {   
14774         var dv = ''; // display value
14775         
14776         if (this.displayField) {
14777             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14778         } else {
14779             // this is an error condition!!!
14780             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14781         }
14782         
14783         if(!dv.length){
14784             return;
14785         }
14786         
14787         var choice = this.choices.createChild({
14788             tag: 'li',
14789             cls: 'roo-select2-search-choice',
14790             cn: [
14791                 {
14792                     tag: 'div',
14793                     html: dv
14794                 },
14795                 {
14796                     tag: 'a',
14797                     href: '#',
14798                     cls: 'roo-select2-search-choice-close fa fa-times',
14799                     tabindex: '-1'
14800                 }
14801             ]
14802             
14803         }, this.searchField);
14804         
14805         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14806         
14807         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14808         
14809         this.item.push(o);
14810         
14811         this.lastData = o;
14812         
14813         this.syncValue();
14814         
14815         this.inputEl().dom.value = '';
14816         
14817         this.validate();
14818     },
14819     
14820     onRemoveItem : function(e, _self, o)
14821     {
14822         e.preventDefault();
14823         
14824         this.lastItem = Roo.apply([], this.item);
14825         
14826         var index = this.item.indexOf(o.data) * 1;
14827         
14828         if( index < 0){
14829             Roo.log('not this item?!');
14830             return;
14831         }
14832         
14833         this.item.splice(index, 1);
14834         o.item.remove();
14835         
14836         this.syncValue();
14837         
14838         this.fireEvent('remove', this, e);
14839         
14840         this.validate();
14841         
14842     },
14843     
14844     syncValue : function()
14845     {
14846         if(!this.item.length){
14847             this.clearValue();
14848             return;
14849         }
14850             
14851         var value = [];
14852         var _this = this;
14853         Roo.each(this.item, function(i){
14854             if(_this.valueField){
14855                 value.push(i[_this.valueField]);
14856                 return;
14857             }
14858
14859             value.push(i);
14860         });
14861
14862         this.value = value.join(',');
14863
14864         if(this.hiddenField){
14865             this.hiddenField.dom.value = this.value;
14866         }
14867         
14868         this.store.fireEvent("datachanged", this.store);
14869         
14870         this.validate();
14871     },
14872     
14873     clearItem : function()
14874     {
14875         if(!this.multiple){
14876             return;
14877         }
14878         
14879         this.item = [];
14880         
14881         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14882            c.remove();
14883         });
14884         
14885         this.syncValue();
14886         
14887         this.validate();
14888         
14889         if(this.tickable && !Roo.isTouch){
14890             this.view.refresh();
14891         }
14892     },
14893     
14894     inputEl: function ()
14895     {
14896         if(Roo.isIOS && this.useNativeIOS){
14897             return this.el.select('select.roo-ios-select', true).first();
14898         }
14899         
14900         if(Roo.isTouch && this.mobileTouchView){
14901             return this.el.select('input.form-control',true).first();
14902         }
14903         
14904         if(this.tickable){
14905             return this.searchField;
14906         }
14907         
14908         return this.el.select('input.form-control',true).first();
14909     },
14910     
14911     onTickableFooterButtonClick : function(e, btn, el)
14912     {
14913         e.preventDefault();
14914         
14915         this.lastItem = Roo.apply([], this.item);
14916         
14917         if(btn && btn.name == 'cancel'){
14918             this.tickItems = Roo.apply([], this.item);
14919             this.collapse();
14920             return;
14921         }
14922         
14923         this.clearItem();
14924         
14925         var _this = this;
14926         
14927         Roo.each(this.tickItems, function(o){
14928             _this.addItem(o);
14929         });
14930         
14931         this.collapse();
14932         
14933     },
14934     
14935     validate : function()
14936     {
14937         if(this.getVisibilityEl().hasClass('hidden')){
14938             return true;
14939         }
14940         
14941         var v = this.getRawValue();
14942         
14943         if(this.multiple){
14944             v = this.getValue();
14945         }
14946         
14947         if(this.disabled || this.allowBlank || v.length){
14948             this.markValid();
14949             return true;
14950         }
14951         
14952         this.markInvalid();
14953         return false;
14954     },
14955     
14956     tickableInputEl : function()
14957     {
14958         if(!this.tickable || !this.editable){
14959             return this.inputEl();
14960         }
14961         
14962         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14963     },
14964     
14965     
14966     getAutoCreateTouchView : function()
14967     {
14968         var id = Roo.id();
14969         
14970         var cfg = {
14971             cls: 'form-group' //input-group
14972         };
14973         
14974         var input =  {
14975             tag: 'input',
14976             id : id,
14977             type : this.inputType,
14978             cls : 'form-control x-combo-noedit',
14979             autocomplete: 'new-password',
14980             placeholder : this.placeholder || '',
14981             readonly : true
14982         };
14983         
14984         if (this.name) {
14985             input.name = this.name;
14986         }
14987         
14988         if (this.size) {
14989             input.cls += ' input-' + this.size;
14990         }
14991         
14992         if (this.disabled) {
14993             input.disabled = true;
14994         }
14995         
14996         var inputblock = {
14997             cls : '',
14998             cn : [
14999                 input
15000             ]
15001         };
15002         
15003         if(this.before){
15004             inputblock.cls += ' input-group';
15005             
15006             inputblock.cn.unshift({
15007                 tag :'span',
15008                 cls : 'input-group-addon',
15009                 html : this.before
15010             });
15011         }
15012         
15013         if(this.removable && !this.multiple){
15014             inputblock.cls += ' roo-removable';
15015             
15016             inputblock.cn.push({
15017                 tag: 'button',
15018                 html : 'x',
15019                 cls : 'roo-combo-removable-btn close'
15020             });
15021         }
15022
15023         if(this.hasFeedback && !this.allowBlank){
15024             
15025             inputblock.cls += ' has-feedback';
15026             
15027             inputblock.cn.push({
15028                 tag: 'span',
15029                 cls: 'glyphicon form-control-feedback'
15030             });
15031             
15032         }
15033         
15034         if (this.after) {
15035             
15036             inputblock.cls += (this.before) ? '' : ' input-group';
15037             
15038             inputblock.cn.push({
15039                 tag :'span',
15040                 cls : 'input-group-addon',
15041                 html : this.after
15042             });
15043         }
15044
15045         var box = {
15046             tag: 'div',
15047             cn: [
15048                 {
15049                     tag: 'input',
15050                     type : 'hidden',
15051                     cls: 'form-hidden-field'
15052                 },
15053                 inputblock
15054             ]
15055             
15056         };
15057         
15058         if(this.multiple){
15059             box = {
15060                 tag: 'div',
15061                 cn: [
15062                     {
15063                         tag: 'input',
15064                         type : 'hidden',
15065                         cls: 'form-hidden-field'
15066                     },
15067                     {
15068                         tag: 'ul',
15069                         cls: 'roo-select2-choices',
15070                         cn:[
15071                             {
15072                                 tag: 'li',
15073                                 cls: 'roo-select2-search-field',
15074                                 cn: [
15075
15076                                     inputblock
15077                                 ]
15078                             }
15079                         ]
15080                     }
15081                 ]
15082             }
15083         };
15084         
15085         var combobox = {
15086             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15087             cn: [
15088                 box
15089             ]
15090         };
15091         
15092         if(!this.multiple && this.showToggleBtn){
15093             
15094             var caret = {
15095                         tag: 'span',
15096                         cls: 'caret'
15097             };
15098             
15099             if (this.caret != false) {
15100                 caret = {
15101                      tag: 'i',
15102                      cls: 'fa fa-' + this.caret
15103                 };
15104                 
15105             }
15106             
15107             combobox.cn.push({
15108                 tag :'span',
15109                 cls : 'input-group-addon btn dropdown-toggle',
15110                 cn : [
15111                     caret,
15112                     {
15113                         tag: 'span',
15114                         cls: 'combobox-clear',
15115                         cn  : [
15116                             {
15117                                 tag : 'i',
15118                                 cls: 'icon-remove'
15119                             }
15120                         ]
15121                     }
15122                 ]
15123
15124             })
15125         }
15126         
15127         if(this.multiple){
15128             combobox.cls += ' roo-select2-container-multi';
15129         }
15130         
15131         var align = this.labelAlign || this.parentLabelAlign();
15132         
15133         if (align ==='left' && this.fieldLabel.length) {
15134
15135             cfg.cn = [
15136                 {
15137                    tag : 'i',
15138                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15139                    tooltip : 'This field is required'
15140                 },
15141                 {
15142                     tag: 'label',
15143                     cls : 'control-label',
15144                     html : this.fieldLabel
15145
15146                 },
15147                 {
15148                     cls : '', 
15149                     cn: [
15150                         combobox
15151                     ]
15152                 }
15153             ];
15154             
15155             var labelCfg = cfg.cn[1];
15156             var contentCfg = cfg.cn[2];
15157             
15158
15159             if(this.indicatorpos == 'right'){
15160                 cfg.cn = [
15161                     {
15162                         tag: 'label',
15163                         'for' :  id,
15164                         cls : 'control-label',
15165                         cn : [
15166                             {
15167                                 tag : 'span',
15168                                 html : this.fieldLabel
15169                             },
15170                             {
15171                                 tag : 'i',
15172                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15173                                 tooltip : 'This field is required'
15174                             }
15175                         ]
15176                     },
15177                     {
15178                         cls : "",
15179                         cn: [
15180                             combobox
15181                         ]
15182                     }
15183
15184                 ];
15185                 
15186                 labelCfg = cfg.cn[0];
15187                 contentCfg = cfg.cn[1];
15188             }
15189             
15190            
15191             
15192             if(this.labelWidth > 12){
15193                 labelCfg.style = "width: " + this.labelWidth + 'px';
15194             }
15195             
15196             if(this.labelWidth < 13 && this.labelmd == 0){
15197                 this.labelmd = this.labelWidth;
15198             }
15199             
15200             if(this.labellg > 0){
15201                 labelCfg.cls += ' col-lg-' + this.labellg;
15202                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15203             }
15204             
15205             if(this.labelmd > 0){
15206                 labelCfg.cls += ' col-md-' + this.labelmd;
15207                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15208             }
15209             
15210             if(this.labelsm > 0){
15211                 labelCfg.cls += ' col-sm-' + this.labelsm;
15212                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15213             }
15214             
15215             if(this.labelxs > 0){
15216                 labelCfg.cls += ' col-xs-' + this.labelxs;
15217                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15218             }
15219                 
15220                 
15221         } else if ( this.fieldLabel.length) {
15222             cfg.cn = [
15223                 {
15224                    tag : 'i',
15225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15226                    tooltip : 'This field is required'
15227                 },
15228                 {
15229                     tag: 'label',
15230                     cls : 'control-label',
15231                     html : this.fieldLabel
15232
15233                 },
15234                 {
15235                     cls : '', 
15236                     cn: [
15237                         combobox
15238                     ]
15239                 }
15240             ];
15241             
15242             if(this.indicatorpos == 'right'){
15243                 cfg.cn = [
15244                     {
15245                         tag: 'label',
15246                         cls : 'control-label',
15247                         html : this.fieldLabel,
15248                         cn : [
15249                             {
15250                                tag : 'i',
15251                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15252                                tooltip : 'This field is required'
15253                             }
15254                         ]
15255                     },
15256                     {
15257                         cls : '', 
15258                         cn: [
15259                             combobox
15260                         ]
15261                     }
15262                 ];
15263             }
15264         } else {
15265             cfg.cn = combobox;    
15266         }
15267         
15268         
15269         var settings = this;
15270         
15271         ['xs','sm','md','lg'].map(function(size){
15272             if (settings[size]) {
15273                 cfg.cls += ' col-' + size + '-' + settings[size];
15274             }
15275         });
15276         
15277         return cfg;
15278     },
15279     
15280     initTouchView : function()
15281     {
15282         this.renderTouchView();
15283         
15284         this.touchViewEl.on('scroll', function(){
15285             this.el.dom.scrollTop = 0;
15286         }, this);
15287         
15288         this.originalValue = this.getValue();
15289         
15290         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15291         
15292         this.inputEl().on("click", this.showTouchView, this);
15293         if (this.triggerEl) {
15294             this.triggerEl.on("click", this.showTouchView, this);
15295         }
15296         
15297         
15298         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15299         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15300         
15301         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15302         
15303         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15304         this.store.on('load', this.onTouchViewLoad, this);
15305         this.store.on('loadexception', this.onTouchViewLoadException, this);
15306         
15307         if(this.hiddenName){
15308             
15309             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15310             
15311             this.hiddenField.dom.value =
15312                 this.hiddenValue !== undefined ? this.hiddenValue :
15313                 this.value !== undefined ? this.value : '';
15314         
15315             this.el.dom.removeAttribute('name');
15316             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15317         }
15318         
15319         if(this.multiple){
15320             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15321             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15322         }
15323         
15324         if(this.removable && !this.multiple){
15325             var close = this.closeTriggerEl();
15326             if(close){
15327                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15328                 close.on('click', this.removeBtnClick, this, close);
15329             }
15330         }
15331         /*
15332          * fix the bug in Safari iOS8
15333          */
15334         this.inputEl().on("focus", function(e){
15335             document.activeElement.blur();
15336         }, this);
15337         
15338         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15339         
15340         return;
15341         
15342         
15343     },
15344     
15345     renderTouchView : function()
15346     {
15347         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15348         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15349         
15350         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15351         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15352         
15353         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15354         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15355         this.touchViewBodyEl.setStyle('overflow', 'auto');
15356         
15357         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15358         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15359         
15360         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15361         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15362         
15363     },
15364     
15365     showTouchView : function()
15366     {
15367         if(this.disabled){
15368             return;
15369         }
15370         
15371         this.touchViewHeaderEl.hide();
15372
15373         if(this.modalTitle.length){
15374             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15375             this.touchViewHeaderEl.show();
15376         }
15377
15378         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15379         this.touchViewEl.show();
15380
15381         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15382         
15383         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15384         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15385
15386         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15387
15388         if(this.modalTitle.length){
15389             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15390         }
15391         
15392         this.touchViewBodyEl.setHeight(bodyHeight);
15393
15394         if(this.animate){
15395             var _this = this;
15396             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15397         }else{
15398             this.touchViewEl.addClass('in');
15399         }
15400         
15401         if(this._touchViewMask){
15402             Roo.get(document.body).addClass("x-body-masked");
15403             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15404             this._touchViewMask.setStyle('z-index', 10000);
15405             this._touchViewMask.addClass('show');
15406         }
15407         
15408         this.doTouchViewQuery();
15409         
15410     },
15411     
15412     hideTouchView : function()
15413     {
15414         this.touchViewEl.removeClass('in');
15415
15416         if(this.animate){
15417             var _this = this;
15418             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15419         }else{
15420             this.touchViewEl.setStyle('display', 'none');
15421         }
15422         
15423         if(this._touchViewMask){
15424             this._touchViewMask.removeClass('show');
15425             Roo.get(document.body).removeClass("x-body-masked");
15426         }
15427     },
15428     
15429     setTouchViewValue : function()
15430     {
15431         if(this.multiple){
15432             this.clearItem();
15433         
15434             var _this = this;
15435
15436             Roo.each(this.tickItems, function(o){
15437                 this.addItem(o);
15438             }, this);
15439         }
15440         
15441         this.hideTouchView();
15442     },
15443     
15444     doTouchViewQuery : function()
15445     {
15446         var qe = {
15447             query: '',
15448             forceAll: true,
15449             combo: this,
15450             cancel:false
15451         };
15452         
15453         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15454             return false;
15455         }
15456         
15457         if(!this.alwaysQuery || this.mode == 'local'){
15458             this.onTouchViewLoad();
15459             return;
15460         }
15461         
15462         this.store.load();
15463     },
15464     
15465     onTouchViewBeforeLoad : function(combo,opts)
15466     {
15467         return;
15468     },
15469
15470     // private
15471     onTouchViewLoad : function()
15472     {
15473         if(this.store.getCount() < 1){
15474             this.onTouchViewEmptyResults();
15475             return;
15476         }
15477         
15478         this.clearTouchView();
15479         
15480         var rawValue = this.getRawValue();
15481         
15482         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15483         
15484         this.tickItems = [];
15485         
15486         this.store.data.each(function(d, rowIndex){
15487             var row = this.touchViewListGroup.createChild(template);
15488             
15489             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15490                 row.addClass(d.data.cls);
15491             }
15492             
15493             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15494                 var cfg = {
15495                     data : d.data,
15496                     html : d.data[this.displayField]
15497                 };
15498                 
15499                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15500                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15501                 }
15502             }
15503             row.removeClass('selected');
15504             if(!this.multiple && this.valueField &&
15505                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15506             {
15507                 // radio buttons..
15508                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15509                 row.addClass('selected');
15510             }
15511             
15512             if(this.multiple && this.valueField &&
15513                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15514             {
15515                 
15516                 // checkboxes...
15517                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15518                 this.tickItems.push(d.data);
15519             }
15520             
15521             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15522             
15523         }, this);
15524         
15525         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15526         
15527         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15528
15529         if(this.modalTitle.length){
15530             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15531         }
15532
15533         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15534         
15535         if(this.mobile_restrict_height && listHeight < bodyHeight){
15536             this.touchViewBodyEl.setHeight(listHeight);
15537         }
15538         
15539         var _this = this;
15540         
15541         if(firstChecked && listHeight > bodyHeight){
15542             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15543         }
15544         
15545     },
15546     
15547     onTouchViewLoadException : function()
15548     {
15549         this.hideTouchView();
15550     },
15551     
15552     onTouchViewEmptyResults : function()
15553     {
15554         this.clearTouchView();
15555         
15556         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15557         
15558         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15559         
15560     },
15561     
15562     clearTouchView : function()
15563     {
15564         this.touchViewListGroup.dom.innerHTML = '';
15565     },
15566     
15567     onTouchViewClick : function(e, el, o)
15568     {
15569         e.preventDefault();
15570         
15571         var row = o.row;
15572         var rowIndex = o.rowIndex;
15573         
15574         var r = this.store.getAt(rowIndex);
15575         
15576         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15577             
15578             if(!this.multiple){
15579                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15580                     c.dom.removeAttribute('checked');
15581                 }, this);
15582
15583                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15584
15585                 this.setFromData(r.data);
15586
15587                 var close = this.closeTriggerEl();
15588
15589                 if(close){
15590                     close.show();
15591                 }
15592
15593                 this.hideTouchView();
15594
15595                 this.fireEvent('select', this, r, rowIndex);
15596
15597                 return;
15598             }
15599
15600             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15601                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15602                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15603                 return;
15604             }
15605
15606             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15607             this.addItem(r.data);
15608             this.tickItems.push(r.data);
15609         }
15610     },
15611     
15612     getAutoCreateNativeIOS : function()
15613     {
15614         var cfg = {
15615             cls: 'form-group' //input-group,
15616         };
15617         
15618         var combobox =  {
15619             tag: 'select',
15620             cls : 'roo-ios-select'
15621         };
15622         
15623         if (this.name) {
15624             combobox.name = this.name;
15625         }
15626         
15627         if (this.disabled) {
15628             combobox.disabled = true;
15629         }
15630         
15631         var settings = this;
15632         
15633         ['xs','sm','md','lg'].map(function(size){
15634             if (settings[size]) {
15635                 cfg.cls += ' col-' + size + '-' + settings[size];
15636             }
15637         });
15638         
15639         cfg.cn = combobox;
15640         
15641         return cfg;
15642         
15643     },
15644     
15645     initIOSView : function()
15646     {
15647         this.store.on('load', this.onIOSViewLoad, this);
15648         
15649         return;
15650     },
15651     
15652     onIOSViewLoad : function()
15653     {
15654         if(this.store.getCount() < 1){
15655             return;
15656         }
15657         
15658         this.clearIOSView();
15659         
15660         if(this.allowBlank) {
15661             
15662             var default_text = '-- SELECT --';
15663             
15664             if(this.placeholder.length){
15665                 default_text = this.placeholder;
15666             }
15667             
15668             if(this.emptyTitle.length){
15669                 default_text += ' - ' + this.emptyTitle + ' -';
15670             }
15671             
15672             var opt = this.inputEl().createChild({
15673                 tag: 'option',
15674                 value : 0,
15675                 html : default_text
15676             });
15677             
15678             var o = {};
15679             o[this.valueField] = 0;
15680             o[this.displayField] = default_text;
15681             
15682             this.ios_options.push({
15683                 data : o,
15684                 el : opt
15685             });
15686             
15687         }
15688         
15689         this.store.data.each(function(d, rowIndex){
15690             
15691             var html = '';
15692             
15693             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15694                 html = d.data[this.displayField];
15695             }
15696             
15697             var value = '';
15698             
15699             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15700                 value = d.data[this.valueField];
15701             }
15702             
15703             var option = {
15704                 tag: 'option',
15705                 value : value,
15706                 html : html
15707             };
15708             
15709             if(this.value == d.data[this.valueField]){
15710                 option['selected'] = true;
15711             }
15712             
15713             var opt = this.inputEl().createChild(option);
15714             
15715             this.ios_options.push({
15716                 data : d.data,
15717                 el : opt
15718             });
15719             
15720         }, this);
15721         
15722         this.inputEl().on('change', function(){
15723            this.fireEvent('select', this);
15724         }, this);
15725         
15726     },
15727     
15728     clearIOSView: function()
15729     {
15730         this.inputEl().dom.innerHTML = '';
15731         
15732         this.ios_options = [];
15733     },
15734     
15735     setIOSValue: function(v)
15736     {
15737         this.value = v;
15738         
15739         if(!this.ios_options){
15740             return;
15741         }
15742         
15743         Roo.each(this.ios_options, function(opts){
15744            
15745            opts.el.dom.removeAttribute('selected');
15746            
15747            if(opts.data[this.valueField] != v){
15748                return;
15749            }
15750            
15751            opts.el.dom.setAttribute('selected', true);
15752            
15753         }, this);
15754     }
15755
15756     /** 
15757     * @cfg {Boolean} grow 
15758     * @hide 
15759     */
15760     /** 
15761     * @cfg {Number} growMin 
15762     * @hide 
15763     */
15764     /** 
15765     * @cfg {Number} growMax 
15766     * @hide 
15767     */
15768     /**
15769      * @hide
15770      * @method autoSize
15771      */
15772 });
15773
15774 Roo.apply(Roo.bootstrap.ComboBox,  {
15775     
15776     header : {
15777         tag: 'div',
15778         cls: 'modal-header',
15779         cn: [
15780             {
15781                 tag: 'h4',
15782                 cls: 'modal-title'
15783             }
15784         ]
15785     },
15786     
15787     body : {
15788         tag: 'div',
15789         cls: 'modal-body',
15790         cn: [
15791             {
15792                 tag: 'ul',
15793                 cls: 'list-group'
15794             }
15795         ]
15796     },
15797     
15798     listItemRadio : {
15799         tag: 'li',
15800         cls: 'list-group-item',
15801         cn: [
15802             {
15803                 tag: 'span',
15804                 cls: 'roo-combobox-list-group-item-value'
15805             },
15806             {
15807                 tag: 'div',
15808                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15809                 cn: [
15810                     {
15811                         tag: 'input',
15812                         type: 'radio'
15813                     },
15814                     {
15815                         tag: 'label'
15816                     }
15817                 ]
15818             }
15819         ]
15820     },
15821     
15822     listItemCheckbox : {
15823         tag: 'li',
15824         cls: 'list-group-item',
15825         cn: [
15826             {
15827                 tag: 'span',
15828                 cls: 'roo-combobox-list-group-item-value'
15829             },
15830             {
15831                 tag: 'div',
15832                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15833                 cn: [
15834                     {
15835                         tag: 'input',
15836                         type: 'checkbox'
15837                     },
15838                     {
15839                         tag: 'label'
15840                     }
15841                 ]
15842             }
15843         ]
15844     },
15845     
15846     emptyResult : {
15847         tag: 'div',
15848         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15849     },
15850     
15851     footer : {
15852         tag: 'div',
15853         cls: 'modal-footer',
15854         cn: [
15855             {
15856                 tag: 'div',
15857                 cls: 'row',
15858                 cn: [
15859                     {
15860                         tag: 'div',
15861                         cls: 'col-xs-6 text-left',
15862                         cn: {
15863                             tag: 'button',
15864                             cls: 'btn btn-danger roo-touch-view-cancel',
15865                             html: 'Cancel'
15866                         }
15867                     },
15868                     {
15869                         tag: 'div',
15870                         cls: 'col-xs-6 text-right',
15871                         cn: {
15872                             tag: 'button',
15873                             cls: 'btn btn-success roo-touch-view-ok',
15874                             html: 'OK'
15875                         }
15876                     }
15877                 ]
15878             }
15879         ]
15880         
15881     }
15882 });
15883
15884 Roo.apply(Roo.bootstrap.ComboBox,  {
15885     
15886     touchViewTemplate : {
15887         tag: 'div',
15888         cls: 'modal fade roo-combobox-touch-view',
15889         cn: [
15890             {
15891                 tag: 'div',
15892                 cls: 'modal-dialog',
15893                 style : 'position:fixed', // we have to fix position....
15894                 cn: [
15895                     {
15896                         tag: 'div',
15897                         cls: 'modal-content',
15898                         cn: [
15899                             Roo.bootstrap.ComboBox.header,
15900                             Roo.bootstrap.ComboBox.body,
15901                             Roo.bootstrap.ComboBox.footer
15902                         ]
15903                     }
15904                 ]
15905             }
15906         ]
15907     }
15908 });/*
15909  * Based on:
15910  * Ext JS Library 1.1.1
15911  * Copyright(c) 2006-2007, Ext JS, LLC.
15912  *
15913  * Originally Released Under LGPL - original licence link has changed is not relivant.
15914  *
15915  * Fork - LGPL
15916  * <script type="text/javascript">
15917  */
15918
15919 /**
15920  * @class Roo.View
15921  * @extends Roo.util.Observable
15922  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15923  * This class also supports single and multi selection modes. <br>
15924  * Create a data model bound view:
15925  <pre><code>
15926  var store = new Roo.data.Store(...);
15927
15928  var view = new Roo.View({
15929     el : "my-element",
15930     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15931  
15932     singleSelect: true,
15933     selectedClass: "ydataview-selected",
15934     store: store
15935  });
15936
15937  // listen for node click?
15938  view.on("click", function(vw, index, node, e){
15939  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15940  });
15941
15942  // load XML data
15943  dataModel.load("foobar.xml");
15944  </code></pre>
15945  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15946  * <br><br>
15947  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15948  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15949  * 
15950  * Note: old style constructor is still suported (container, template, config)
15951  * 
15952  * @constructor
15953  * Create a new View
15954  * @param {Object} config The config object
15955  * 
15956  */
15957 Roo.View = function(config, depreciated_tpl, depreciated_config){
15958     
15959     this.parent = false;
15960     
15961     if (typeof(depreciated_tpl) == 'undefined') {
15962         // new way.. - universal constructor.
15963         Roo.apply(this, config);
15964         this.el  = Roo.get(this.el);
15965     } else {
15966         // old format..
15967         this.el  = Roo.get(config);
15968         this.tpl = depreciated_tpl;
15969         Roo.apply(this, depreciated_config);
15970     }
15971     this.wrapEl  = this.el.wrap().wrap();
15972     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15973     
15974     
15975     if(typeof(this.tpl) == "string"){
15976         this.tpl = new Roo.Template(this.tpl);
15977     } else {
15978         // support xtype ctors..
15979         this.tpl = new Roo.factory(this.tpl, Roo);
15980     }
15981     
15982     
15983     this.tpl.compile();
15984     
15985     /** @private */
15986     this.addEvents({
15987         /**
15988          * @event beforeclick
15989          * Fires before a click is processed. Returns false to cancel the default action.
15990          * @param {Roo.View} this
15991          * @param {Number} index The index of the target node
15992          * @param {HTMLElement} node The target node
15993          * @param {Roo.EventObject} e The raw event object
15994          */
15995             "beforeclick" : true,
15996         /**
15997          * @event click
15998          * Fires when a template node is clicked.
15999          * @param {Roo.View} this
16000          * @param {Number} index The index of the target node
16001          * @param {HTMLElement} node The target node
16002          * @param {Roo.EventObject} e The raw event object
16003          */
16004             "click" : true,
16005         /**
16006          * @event dblclick
16007          * Fires when a template node is double clicked.
16008          * @param {Roo.View} this
16009          * @param {Number} index The index of the target node
16010          * @param {HTMLElement} node The target node
16011          * @param {Roo.EventObject} e The raw event object
16012          */
16013             "dblclick" : true,
16014         /**
16015          * @event contextmenu
16016          * Fires when a template node is right clicked.
16017          * @param {Roo.View} this
16018          * @param {Number} index The index of the target node
16019          * @param {HTMLElement} node The target node
16020          * @param {Roo.EventObject} e The raw event object
16021          */
16022             "contextmenu" : true,
16023         /**
16024          * @event selectionchange
16025          * Fires when the selected nodes change.
16026          * @param {Roo.View} this
16027          * @param {Array} selections Array of the selected nodes
16028          */
16029             "selectionchange" : true,
16030     
16031         /**
16032          * @event beforeselect
16033          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16034          * @param {Roo.View} this
16035          * @param {HTMLElement} node The node to be selected
16036          * @param {Array} selections Array of currently selected nodes
16037          */
16038             "beforeselect" : true,
16039         /**
16040          * @event preparedata
16041          * Fires on every row to render, to allow you to change the data.
16042          * @param {Roo.View} this
16043          * @param {Object} data to be rendered (change this)
16044          */
16045           "preparedata" : true
16046           
16047           
16048         });
16049
16050
16051
16052     this.el.on({
16053         "click": this.onClick,
16054         "dblclick": this.onDblClick,
16055         "contextmenu": this.onContextMenu,
16056         scope:this
16057     });
16058
16059     this.selections = [];
16060     this.nodes = [];
16061     this.cmp = new Roo.CompositeElementLite([]);
16062     if(this.store){
16063         this.store = Roo.factory(this.store, Roo.data);
16064         this.setStore(this.store, true);
16065     }
16066     
16067     if ( this.footer && this.footer.xtype) {
16068            
16069          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16070         
16071         this.footer.dataSource = this.store;
16072         this.footer.container = fctr;
16073         this.footer = Roo.factory(this.footer, Roo);
16074         fctr.insertFirst(this.el);
16075         
16076         // this is a bit insane - as the paging toolbar seems to detach the el..
16077 //        dom.parentNode.parentNode.parentNode
16078          // they get detached?
16079     }
16080     
16081     
16082     Roo.View.superclass.constructor.call(this);
16083     
16084     
16085 };
16086
16087 Roo.extend(Roo.View, Roo.util.Observable, {
16088     
16089      /**
16090      * @cfg {Roo.data.Store} store Data store to load data from.
16091      */
16092     store : false,
16093     
16094     /**
16095      * @cfg {String|Roo.Element} el The container element.
16096      */
16097     el : '',
16098     
16099     /**
16100      * @cfg {String|Roo.Template} tpl The template used by this View 
16101      */
16102     tpl : false,
16103     /**
16104      * @cfg {String} dataName the named area of the template to use as the data area
16105      *                          Works with domtemplates roo-name="name"
16106      */
16107     dataName: false,
16108     /**
16109      * @cfg {String} selectedClass The css class to add to selected nodes
16110      */
16111     selectedClass : "x-view-selected",
16112      /**
16113      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16114      */
16115     emptyText : "",
16116     
16117     /**
16118      * @cfg {String} text to display on mask (default Loading)
16119      */
16120     mask : false,
16121     /**
16122      * @cfg {Boolean} multiSelect Allow multiple selection
16123      */
16124     multiSelect : false,
16125     /**
16126      * @cfg {Boolean} singleSelect Allow single selection
16127      */
16128     singleSelect:  false,
16129     
16130     /**
16131      * @cfg {Boolean} toggleSelect - selecting 
16132      */
16133     toggleSelect : false,
16134     
16135     /**
16136      * @cfg {Boolean} tickable - selecting 
16137      */
16138     tickable : false,
16139     
16140     /**
16141      * Returns the element this view is bound to.
16142      * @return {Roo.Element}
16143      */
16144     getEl : function(){
16145         return this.wrapEl;
16146     },
16147     
16148     
16149
16150     /**
16151      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16152      */
16153     refresh : function(){
16154         //Roo.log('refresh');
16155         var t = this.tpl;
16156         
16157         // if we are using something like 'domtemplate', then
16158         // the what gets used is:
16159         // t.applySubtemplate(NAME, data, wrapping data..)
16160         // the outer template then get' applied with
16161         //     the store 'extra data'
16162         // and the body get's added to the
16163         //      roo-name="data" node?
16164         //      <span class='roo-tpl-{name}'></span> ?????
16165         
16166         
16167         
16168         this.clearSelections();
16169         this.el.update("");
16170         var html = [];
16171         var records = this.store.getRange();
16172         if(records.length < 1) {
16173             
16174             // is this valid??  = should it render a template??
16175             
16176             this.el.update(this.emptyText);
16177             return;
16178         }
16179         var el = this.el;
16180         if (this.dataName) {
16181             this.el.update(t.apply(this.store.meta)); //????
16182             el = this.el.child('.roo-tpl-' + this.dataName);
16183         }
16184         
16185         for(var i = 0, len = records.length; i < len; i++){
16186             var data = this.prepareData(records[i].data, i, records[i]);
16187             this.fireEvent("preparedata", this, data, i, records[i]);
16188             
16189             var d = Roo.apply({}, data);
16190             
16191             if(this.tickable){
16192                 Roo.apply(d, {'roo-id' : Roo.id()});
16193                 
16194                 var _this = this;
16195             
16196                 Roo.each(this.parent.item, function(item){
16197                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16198                         return;
16199                     }
16200                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16201                 });
16202             }
16203             
16204             html[html.length] = Roo.util.Format.trim(
16205                 this.dataName ?
16206                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16207                     t.apply(d)
16208             );
16209         }
16210         
16211         
16212         
16213         el.update(html.join(""));
16214         this.nodes = el.dom.childNodes;
16215         this.updateIndexes(0);
16216     },
16217     
16218
16219     /**
16220      * Function to override to reformat the data that is sent to
16221      * the template for each node.
16222      * DEPRICATED - use the preparedata event handler.
16223      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16224      * a JSON object for an UpdateManager bound view).
16225      */
16226     prepareData : function(data, index, record)
16227     {
16228         this.fireEvent("preparedata", this, data, index, record);
16229         return data;
16230     },
16231
16232     onUpdate : function(ds, record){
16233         // Roo.log('on update');   
16234         this.clearSelections();
16235         var index = this.store.indexOf(record);
16236         var n = this.nodes[index];
16237         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16238         n.parentNode.removeChild(n);
16239         this.updateIndexes(index, index);
16240     },
16241
16242     
16243     
16244 // --------- FIXME     
16245     onAdd : function(ds, records, index)
16246     {
16247         //Roo.log(['on Add', ds, records, index] );        
16248         this.clearSelections();
16249         if(this.nodes.length == 0){
16250             this.refresh();
16251             return;
16252         }
16253         var n = this.nodes[index];
16254         for(var i = 0, len = records.length; i < len; i++){
16255             var d = this.prepareData(records[i].data, i, records[i]);
16256             if(n){
16257                 this.tpl.insertBefore(n, d);
16258             }else{
16259                 
16260                 this.tpl.append(this.el, d);
16261             }
16262         }
16263         this.updateIndexes(index);
16264     },
16265
16266     onRemove : function(ds, record, index){
16267        // Roo.log('onRemove');
16268         this.clearSelections();
16269         var el = this.dataName  ?
16270             this.el.child('.roo-tpl-' + this.dataName) :
16271             this.el; 
16272         
16273         el.dom.removeChild(this.nodes[index]);
16274         this.updateIndexes(index);
16275     },
16276
16277     /**
16278      * Refresh an individual node.
16279      * @param {Number} index
16280      */
16281     refreshNode : function(index){
16282         this.onUpdate(this.store, this.store.getAt(index));
16283     },
16284
16285     updateIndexes : function(startIndex, endIndex){
16286         var ns = this.nodes;
16287         startIndex = startIndex || 0;
16288         endIndex = endIndex || ns.length - 1;
16289         for(var i = startIndex; i <= endIndex; i++){
16290             ns[i].nodeIndex = i;
16291         }
16292     },
16293
16294     /**
16295      * Changes the data store this view uses and refresh the view.
16296      * @param {Store} store
16297      */
16298     setStore : function(store, initial){
16299         if(!initial && this.store){
16300             this.store.un("datachanged", this.refresh);
16301             this.store.un("add", this.onAdd);
16302             this.store.un("remove", this.onRemove);
16303             this.store.un("update", this.onUpdate);
16304             this.store.un("clear", this.refresh);
16305             this.store.un("beforeload", this.onBeforeLoad);
16306             this.store.un("load", this.onLoad);
16307             this.store.un("loadexception", this.onLoad);
16308         }
16309         if(store){
16310           
16311             store.on("datachanged", this.refresh, this);
16312             store.on("add", this.onAdd, this);
16313             store.on("remove", this.onRemove, this);
16314             store.on("update", this.onUpdate, this);
16315             store.on("clear", this.refresh, this);
16316             store.on("beforeload", this.onBeforeLoad, this);
16317             store.on("load", this.onLoad, this);
16318             store.on("loadexception", this.onLoad, this);
16319         }
16320         
16321         if(store){
16322             this.refresh();
16323         }
16324     },
16325     /**
16326      * onbeforeLoad - masks the loading area.
16327      *
16328      */
16329     onBeforeLoad : function(store,opts)
16330     {
16331          //Roo.log('onBeforeLoad');   
16332         if (!opts.add) {
16333             this.el.update("");
16334         }
16335         this.el.mask(this.mask ? this.mask : "Loading" ); 
16336     },
16337     onLoad : function ()
16338     {
16339         this.el.unmask();
16340     },
16341     
16342
16343     /**
16344      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16345      * @param {HTMLElement} node
16346      * @return {HTMLElement} The template node
16347      */
16348     findItemFromChild : function(node){
16349         var el = this.dataName  ?
16350             this.el.child('.roo-tpl-' + this.dataName,true) :
16351             this.el.dom; 
16352         
16353         if(!node || node.parentNode == el){
16354                     return node;
16355             }
16356             var p = node.parentNode;
16357             while(p && p != el){
16358             if(p.parentNode == el){
16359                 return p;
16360             }
16361             p = p.parentNode;
16362         }
16363             return null;
16364     },
16365
16366     /** @ignore */
16367     onClick : function(e){
16368         var item = this.findItemFromChild(e.getTarget());
16369         if(item){
16370             var index = this.indexOf(item);
16371             if(this.onItemClick(item, index, e) !== false){
16372                 this.fireEvent("click", this, index, item, e);
16373             }
16374         }else{
16375             this.clearSelections();
16376         }
16377     },
16378
16379     /** @ignore */
16380     onContextMenu : function(e){
16381         var item = this.findItemFromChild(e.getTarget());
16382         if(item){
16383             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16384         }
16385     },
16386
16387     /** @ignore */
16388     onDblClick : function(e){
16389         var item = this.findItemFromChild(e.getTarget());
16390         if(item){
16391             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16392         }
16393     },
16394
16395     onItemClick : function(item, index, e)
16396     {
16397         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16398             return false;
16399         }
16400         if (this.toggleSelect) {
16401             var m = this.isSelected(item) ? 'unselect' : 'select';
16402             //Roo.log(m);
16403             var _t = this;
16404             _t[m](item, true, false);
16405             return true;
16406         }
16407         if(this.multiSelect || this.singleSelect){
16408             if(this.multiSelect && e.shiftKey && this.lastSelection){
16409                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16410             }else{
16411                 this.select(item, this.multiSelect && e.ctrlKey);
16412                 this.lastSelection = item;
16413             }
16414             
16415             if(!this.tickable){
16416                 e.preventDefault();
16417             }
16418             
16419         }
16420         return true;
16421     },
16422
16423     /**
16424      * Get the number of selected nodes.
16425      * @return {Number}
16426      */
16427     getSelectionCount : function(){
16428         return this.selections.length;
16429     },
16430
16431     /**
16432      * Get the currently selected nodes.
16433      * @return {Array} An array of HTMLElements
16434      */
16435     getSelectedNodes : function(){
16436         return this.selections;
16437     },
16438
16439     /**
16440      * Get the indexes of the selected nodes.
16441      * @return {Array}
16442      */
16443     getSelectedIndexes : function(){
16444         var indexes = [], s = this.selections;
16445         for(var i = 0, len = s.length; i < len; i++){
16446             indexes.push(s[i].nodeIndex);
16447         }
16448         return indexes;
16449     },
16450
16451     /**
16452      * Clear all selections
16453      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16454      */
16455     clearSelections : function(suppressEvent){
16456         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16457             this.cmp.elements = this.selections;
16458             this.cmp.removeClass(this.selectedClass);
16459             this.selections = [];
16460             if(!suppressEvent){
16461                 this.fireEvent("selectionchange", this, this.selections);
16462             }
16463         }
16464     },
16465
16466     /**
16467      * Returns true if the passed node is selected
16468      * @param {HTMLElement/Number} node The node or node index
16469      * @return {Boolean}
16470      */
16471     isSelected : function(node){
16472         var s = this.selections;
16473         if(s.length < 1){
16474             return false;
16475         }
16476         node = this.getNode(node);
16477         return s.indexOf(node) !== -1;
16478     },
16479
16480     /**
16481      * Selects nodes.
16482      * @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
16483      * @param {Boolean} keepExisting (optional) true to keep existing selections
16484      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16485      */
16486     select : function(nodeInfo, keepExisting, suppressEvent){
16487         if(nodeInfo instanceof Array){
16488             if(!keepExisting){
16489                 this.clearSelections(true);
16490             }
16491             for(var i = 0, len = nodeInfo.length; i < len; i++){
16492                 this.select(nodeInfo[i], true, true);
16493             }
16494             return;
16495         } 
16496         var node = this.getNode(nodeInfo);
16497         if(!node || this.isSelected(node)){
16498             return; // already selected.
16499         }
16500         if(!keepExisting){
16501             this.clearSelections(true);
16502         }
16503         
16504         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16505             Roo.fly(node).addClass(this.selectedClass);
16506             this.selections.push(node);
16507             if(!suppressEvent){
16508                 this.fireEvent("selectionchange", this, this.selections);
16509             }
16510         }
16511         
16512         
16513     },
16514       /**
16515      * Unselects nodes.
16516      * @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
16517      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16518      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16519      */
16520     unselect : function(nodeInfo, keepExisting, suppressEvent)
16521     {
16522         if(nodeInfo instanceof Array){
16523             Roo.each(this.selections, function(s) {
16524                 this.unselect(s, nodeInfo);
16525             }, this);
16526             return;
16527         }
16528         var node = this.getNode(nodeInfo);
16529         if(!node || !this.isSelected(node)){
16530             //Roo.log("not selected");
16531             return; // not selected.
16532         }
16533         // fireevent???
16534         var ns = [];
16535         Roo.each(this.selections, function(s) {
16536             if (s == node ) {
16537                 Roo.fly(node).removeClass(this.selectedClass);
16538
16539                 return;
16540             }
16541             ns.push(s);
16542         },this);
16543         
16544         this.selections= ns;
16545         this.fireEvent("selectionchange", this, this.selections);
16546     },
16547
16548     /**
16549      * Gets a template node.
16550      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16551      * @return {HTMLElement} The node or null if it wasn't found
16552      */
16553     getNode : function(nodeInfo){
16554         if(typeof nodeInfo == "string"){
16555             return document.getElementById(nodeInfo);
16556         }else if(typeof nodeInfo == "number"){
16557             return this.nodes[nodeInfo];
16558         }
16559         return nodeInfo;
16560     },
16561
16562     /**
16563      * Gets a range template nodes.
16564      * @param {Number} startIndex
16565      * @param {Number} endIndex
16566      * @return {Array} An array of nodes
16567      */
16568     getNodes : function(start, end){
16569         var ns = this.nodes;
16570         start = start || 0;
16571         end = typeof end == "undefined" ? ns.length - 1 : end;
16572         var nodes = [];
16573         if(start <= end){
16574             for(var i = start; i <= end; i++){
16575                 nodes.push(ns[i]);
16576             }
16577         } else{
16578             for(var i = start; i >= end; i--){
16579                 nodes.push(ns[i]);
16580             }
16581         }
16582         return nodes;
16583     },
16584
16585     /**
16586      * Finds the index of the passed node
16587      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16588      * @return {Number} The index of the node or -1
16589      */
16590     indexOf : function(node){
16591         node = this.getNode(node);
16592         if(typeof node.nodeIndex == "number"){
16593             return node.nodeIndex;
16594         }
16595         var ns = this.nodes;
16596         for(var i = 0, len = ns.length; i < len; i++){
16597             if(ns[i] == node){
16598                 return i;
16599             }
16600         }
16601         return -1;
16602     }
16603 });
16604 /*
16605  * - LGPL
16606  *
16607  * based on jquery fullcalendar
16608  * 
16609  */
16610
16611 Roo.bootstrap = Roo.bootstrap || {};
16612 /**
16613  * @class Roo.bootstrap.Calendar
16614  * @extends Roo.bootstrap.Component
16615  * Bootstrap Calendar class
16616  * @cfg {Boolean} loadMask (true|false) default false
16617  * @cfg {Object} header generate the user specific header of the calendar, default false
16618
16619  * @constructor
16620  * Create a new Container
16621  * @param {Object} config The config object
16622  */
16623
16624
16625
16626 Roo.bootstrap.Calendar = function(config){
16627     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16628      this.addEvents({
16629         /**
16630              * @event select
16631              * Fires when a date is selected
16632              * @param {DatePicker} this
16633              * @param {Date} date The selected date
16634              */
16635         'select': true,
16636         /**
16637              * @event monthchange
16638              * Fires when the displayed month changes 
16639              * @param {DatePicker} this
16640              * @param {Date} date The selected month
16641              */
16642         'monthchange': true,
16643         /**
16644              * @event evententer
16645              * Fires when mouse over an event
16646              * @param {Calendar} this
16647              * @param {event} Event
16648              */
16649         'evententer': true,
16650         /**
16651              * @event eventleave
16652              * Fires when the mouse leaves an
16653              * @param {Calendar} this
16654              * @param {event}
16655              */
16656         'eventleave': true,
16657         /**
16658              * @event eventclick
16659              * Fires when the mouse click an
16660              * @param {Calendar} this
16661              * @param {event}
16662              */
16663         'eventclick': true
16664         
16665     });
16666
16667 };
16668
16669 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16670     
16671      /**
16672      * @cfg {Number} startDay
16673      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16674      */
16675     startDay : 0,
16676     
16677     loadMask : false,
16678     
16679     header : false,
16680       
16681     getAutoCreate : function(){
16682         
16683         
16684         var fc_button = function(name, corner, style, content ) {
16685             return Roo.apply({},{
16686                 tag : 'span',
16687                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16688                          (corner.length ?
16689                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16690                             ''
16691                         ),
16692                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16693                 unselectable: 'on'
16694             });
16695         };
16696         
16697         var header = {};
16698         
16699         if(!this.header){
16700             header = {
16701                 tag : 'table',
16702                 cls : 'fc-header',
16703                 style : 'width:100%',
16704                 cn : [
16705                     {
16706                         tag: 'tr',
16707                         cn : [
16708                             {
16709                                 tag : 'td',
16710                                 cls : 'fc-header-left',
16711                                 cn : [
16712                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16713                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16714                                     { tag: 'span', cls: 'fc-header-space' },
16715                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16716
16717
16718                                 ]
16719                             },
16720
16721                             {
16722                                 tag : 'td',
16723                                 cls : 'fc-header-center',
16724                                 cn : [
16725                                     {
16726                                         tag: 'span',
16727                                         cls: 'fc-header-title',
16728                                         cn : {
16729                                             tag: 'H2',
16730                                             html : 'month / year'
16731                                         }
16732                                     }
16733
16734                                 ]
16735                             },
16736                             {
16737                                 tag : 'td',
16738                                 cls : 'fc-header-right',
16739                                 cn : [
16740                               /*      fc_button('month', 'left', '', 'month' ),
16741                                     fc_button('week', '', '', 'week' ),
16742                                     fc_button('day', 'right', '', 'day' )
16743                                 */    
16744
16745                                 ]
16746                             }
16747
16748                         ]
16749                     }
16750                 ]
16751             };
16752         }
16753         
16754         header = this.header;
16755         
16756        
16757         var cal_heads = function() {
16758             var ret = [];
16759             // fixme - handle this.
16760             
16761             for (var i =0; i < Date.dayNames.length; i++) {
16762                 var d = Date.dayNames[i];
16763                 ret.push({
16764                     tag: 'th',
16765                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16766                     html : d.substring(0,3)
16767                 });
16768                 
16769             }
16770             ret[0].cls += ' fc-first';
16771             ret[6].cls += ' fc-last';
16772             return ret;
16773         };
16774         var cal_cell = function(n) {
16775             return  {
16776                 tag: 'td',
16777                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16778                 cn : [
16779                     {
16780                         cn : [
16781                             {
16782                                 cls: 'fc-day-number',
16783                                 html: 'D'
16784                             },
16785                             {
16786                                 cls: 'fc-day-content',
16787                              
16788                                 cn : [
16789                                      {
16790                                         style: 'position: relative;' // height: 17px;
16791                                     }
16792                                 ]
16793                             }
16794                             
16795                             
16796                         ]
16797                     }
16798                 ]
16799                 
16800             }
16801         };
16802         var cal_rows = function() {
16803             
16804             var ret = [];
16805             for (var r = 0; r < 6; r++) {
16806                 var row= {
16807                     tag : 'tr',
16808                     cls : 'fc-week',
16809                     cn : []
16810                 };
16811                 
16812                 for (var i =0; i < Date.dayNames.length; i++) {
16813                     var d = Date.dayNames[i];
16814                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16815
16816                 }
16817                 row.cn[0].cls+=' fc-first';
16818                 row.cn[0].cn[0].style = 'min-height:90px';
16819                 row.cn[6].cls+=' fc-last';
16820                 ret.push(row);
16821                 
16822             }
16823             ret[0].cls += ' fc-first';
16824             ret[4].cls += ' fc-prev-last';
16825             ret[5].cls += ' fc-last';
16826             return ret;
16827             
16828         };
16829         
16830         var cal_table = {
16831             tag: 'table',
16832             cls: 'fc-border-separate',
16833             style : 'width:100%',
16834             cellspacing  : 0,
16835             cn : [
16836                 { 
16837                     tag: 'thead',
16838                     cn : [
16839                         { 
16840                             tag: 'tr',
16841                             cls : 'fc-first fc-last',
16842                             cn : cal_heads()
16843                         }
16844                     ]
16845                 },
16846                 { 
16847                     tag: 'tbody',
16848                     cn : cal_rows()
16849                 }
16850                   
16851             ]
16852         };
16853          
16854          var cfg = {
16855             cls : 'fc fc-ltr',
16856             cn : [
16857                 header,
16858                 {
16859                     cls : 'fc-content',
16860                     style : "position: relative;",
16861                     cn : [
16862                         {
16863                             cls : 'fc-view fc-view-month fc-grid',
16864                             style : 'position: relative',
16865                             unselectable : 'on',
16866                             cn : [
16867                                 {
16868                                     cls : 'fc-event-container',
16869                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16870                                 },
16871                                 cal_table
16872                             ]
16873                         }
16874                     ]
16875     
16876                 }
16877            ] 
16878             
16879         };
16880         
16881          
16882         
16883         return cfg;
16884     },
16885     
16886     
16887     initEvents : function()
16888     {
16889         if(!this.store){
16890             throw "can not find store for calendar";
16891         }
16892         
16893         var mark = {
16894             tag: "div",
16895             cls:"x-dlg-mask",
16896             style: "text-align:center",
16897             cn: [
16898                 {
16899                     tag: "div",
16900                     style: "background-color:white;width:50%;margin:250 auto",
16901                     cn: [
16902                         {
16903                             tag: "img",
16904                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16905                         },
16906                         {
16907                             tag: "span",
16908                             html: "Loading"
16909                         }
16910                         
16911                     ]
16912                 }
16913             ]
16914         };
16915         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16916         
16917         var size = this.el.select('.fc-content', true).first().getSize();
16918         this.maskEl.setSize(size.width, size.height);
16919         this.maskEl.enableDisplayMode("block");
16920         if(!this.loadMask){
16921             this.maskEl.hide();
16922         }
16923         
16924         this.store = Roo.factory(this.store, Roo.data);
16925         this.store.on('load', this.onLoad, this);
16926         this.store.on('beforeload', this.onBeforeLoad, this);
16927         
16928         this.resize();
16929         
16930         this.cells = this.el.select('.fc-day',true);
16931         //Roo.log(this.cells);
16932         this.textNodes = this.el.query('.fc-day-number');
16933         this.cells.addClassOnOver('fc-state-hover');
16934         
16935         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16936         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16937         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16938         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16939         
16940         this.on('monthchange', this.onMonthChange, this);
16941         
16942         this.update(new Date().clearTime());
16943     },
16944     
16945     resize : function() {
16946         var sz  = this.el.getSize();
16947         
16948         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16949         this.el.select('.fc-day-content div',true).setHeight(34);
16950     },
16951     
16952     
16953     // private
16954     showPrevMonth : function(e){
16955         this.update(this.activeDate.add("mo", -1));
16956     },
16957     showToday : function(e){
16958         this.update(new Date().clearTime());
16959     },
16960     // private
16961     showNextMonth : function(e){
16962         this.update(this.activeDate.add("mo", 1));
16963     },
16964
16965     // private
16966     showPrevYear : function(){
16967         this.update(this.activeDate.add("y", -1));
16968     },
16969
16970     // private
16971     showNextYear : function(){
16972         this.update(this.activeDate.add("y", 1));
16973     },
16974
16975     
16976    // private
16977     update : function(date)
16978     {
16979         var vd = this.activeDate;
16980         this.activeDate = date;
16981 //        if(vd && this.el){
16982 //            var t = date.getTime();
16983 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16984 //                Roo.log('using add remove');
16985 //                
16986 //                this.fireEvent('monthchange', this, date);
16987 //                
16988 //                this.cells.removeClass("fc-state-highlight");
16989 //                this.cells.each(function(c){
16990 //                   if(c.dateValue == t){
16991 //                       c.addClass("fc-state-highlight");
16992 //                       setTimeout(function(){
16993 //                            try{c.dom.firstChild.focus();}catch(e){}
16994 //                       }, 50);
16995 //                       return false;
16996 //                   }
16997 //                   return true;
16998 //                });
16999 //                return;
17000 //            }
17001 //        }
17002         
17003         var days = date.getDaysInMonth();
17004         
17005         var firstOfMonth = date.getFirstDateOfMonth();
17006         var startingPos = firstOfMonth.getDay()-this.startDay;
17007         
17008         if(startingPos < this.startDay){
17009             startingPos += 7;
17010         }
17011         
17012         var pm = date.add(Date.MONTH, -1);
17013         var prevStart = pm.getDaysInMonth()-startingPos;
17014 //        
17015         this.cells = this.el.select('.fc-day',true);
17016         this.textNodes = this.el.query('.fc-day-number');
17017         this.cells.addClassOnOver('fc-state-hover');
17018         
17019         var cells = this.cells.elements;
17020         var textEls = this.textNodes;
17021         
17022         Roo.each(cells, function(cell){
17023             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17024         });
17025         
17026         days += startingPos;
17027
17028         // convert everything to numbers so it's fast
17029         var day = 86400000;
17030         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17031         //Roo.log(d);
17032         //Roo.log(pm);
17033         //Roo.log(prevStart);
17034         
17035         var today = new Date().clearTime().getTime();
17036         var sel = date.clearTime().getTime();
17037         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17038         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17039         var ddMatch = this.disabledDatesRE;
17040         var ddText = this.disabledDatesText;
17041         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17042         var ddaysText = this.disabledDaysText;
17043         var format = this.format;
17044         
17045         var setCellClass = function(cal, cell){
17046             cell.row = 0;
17047             cell.events = [];
17048             cell.more = [];
17049             //Roo.log('set Cell Class');
17050             cell.title = "";
17051             var t = d.getTime();
17052             
17053             //Roo.log(d);
17054             
17055             cell.dateValue = t;
17056             if(t == today){
17057                 cell.className += " fc-today";
17058                 cell.className += " fc-state-highlight";
17059                 cell.title = cal.todayText;
17060             }
17061             if(t == sel){
17062                 // disable highlight in other month..
17063                 //cell.className += " fc-state-highlight";
17064                 
17065             }
17066             // disabling
17067             if(t < min) {
17068                 cell.className = " fc-state-disabled";
17069                 cell.title = cal.minText;
17070                 return;
17071             }
17072             if(t > max) {
17073                 cell.className = " fc-state-disabled";
17074                 cell.title = cal.maxText;
17075                 return;
17076             }
17077             if(ddays){
17078                 if(ddays.indexOf(d.getDay()) != -1){
17079                     cell.title = ddaysText;
17080                     cell.className = " fc-state-disabled";
17081                 }
17082             }
17083             if(ddMatch && format){
17084                 var fvalue = d.dateFormat(format);
17085                 if(ddMatch.test(fvalue)){
17086                     cell.title = ddText.replace("%0", fvalue);
17087                     cell.className = " fc-state-disabled";
17088                 }
17089             }
17090             
17091             if (!cell.initialClassName) {
17092                 cell.initialClassName = cell.dom.className;
17093             }
17094             
17095             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17096         };
17097
17098         var i = 0;
17099         
17100         for(; i < startingPos; i++) {
17101             textEls[i].innerHTML = (++prevStart);
17102             d.setDate(d.getDate()+1);
17103             
17104             cells[i].className = "fc-past fc-other-month";
17105             setCellClass(this, cells[i]);
17106         }
17107         
17108         var intDay = 0;
17109         
17110         for(; i < days; i++){
17111             intDay = i - startingPos + 1;
17112             textEls[i].innerHTML = (intDay);
17113             d.setDate(d.getDate()+1);
17114             
17115             cells[i].className = ''; // "x-date-active";
17116             setCellClass(this, cells[i]);
17117         }
17118         var extraDays = 0;
17119         
17120         for(; i < 42; i++) {
17121             textEls[i].innerHTML = (++extraDays);
17122             d.setDate(d.getDate()+1);
17123             
17124             cells[i].className = "fc-future fc-other-month";
17125             setCellClass(this, cells[i]);
17126         }
17127         
17128         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17129         
17130         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17131         
17132         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17133         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17134         
17135         if(totalRows != 6){
17136             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17137             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17138         }
17139         
17140         this.fireEvent('monthchange', this, date);
17141         
17142         
17143         /*
17144         if(!this.internalRender){
17145             var main = this.el.dom.firstChild;
17146             var w = main.offsetWidth;
17147             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17148             Roo.fly(main).setWidth(w);
17149             this.internalRender = true;
17150             // opera does not respect the auto grow header center column
17151             // then, after it gets a width opera refuses to recalculate
17152             // without a second pass
17153             if(Roo.isOpera && !this.secondPass){
17154                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17155                 this.secondPass = true;
17156                 this.update.defer(10, this, [date]);
17157             }
17158         }
17159         */
17160         
17161     },
17162     
17163     findCell : function(dt) {
17164         dt = dt.clearTime().getTime();
17165         var ret = false;
17166         this.cells.each(function(c){
17167             //Roo.log("check " +c.dateValue + '?=' + dt);
17168             if(c.dateValue == dt){
17169                 ret = c;
17170                 return false;
17171             }
17172             return true;
17173         });
17174         
17175         return ret;
17176     },
17177     
17178     findCells : function(ev) {
17179         var s = ev.start.clone().clearTime().getTime();
17180        // Roo.log(s);
17181         var e= ev.end.clone().clearTime().getTime();
17182        // Roo.log(e);
17183         var ret = [];
17184         this.cells.each(function(c){
17185              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17186             
17187             if(c.dateValue > e){
17188                 return ;
17189             }
17190             if(c.dateValue < s){
17191                 return ;
17192             }
17193             ret.push(c);
17194         });
17195         
17196         return ret;    
17197     },
17198     
17199 //    findBestRow: function(cells)
17200 //    {
17201 //        var ret = 0;
17202 //        
17203 //        for (var i =0 ; i < cells.length;i++) {
17204 //            ret  = Math.max(cells[i].rows || 0,ret);
17205 //        }
17206 //        return ret;
17207 //        
17208 //    },
17209     
17210     
17211     addItem : function(ev)
17212     {
17213         // look for vertical location slot in
17214         var cells = this.findCells(ev);
17215         
17216 //        ev.row = this.findBestRow(cells);
17217         
17218         // work out the location.
17219         
17220         var crow = false;
17221         var rows = [];
17222         for(var i =0; i < cells.length; i++) {
17223             
17224             cells[i].row = cells[0].row;
17225             
17226             if(i == 0){
17227                 cells[i].row = cells[i].row + 1;
17228             }
17229             
17230             if (!crow) {
17231                 crow = {
17232                     start : cells[i],
17233                     end :  cells[i]
17234                 };
17235                 continue;
17236             }
17237             if (crow.start.getY() == cells[i].getY()) {
17238                 // on same row.
17239                 crow.end = cells[i];
17240                 continue;
17241             }
17242             // different row.
17243             rows.push(crow);
17244             crow = {
17245                 start: cells[i],
17246                 end : cells[i]
17247             };
17248             
17249         }
17250         
17251         rows.push(crow);
17252         ev.els = [];
17253         ev.rows = rows;
17254         ev.cells = cells;
17255         
17256         cells[0].events.push(ev);
17257         
17258         this.calevents.push(ev);
17259     },
17260     
17261     clearEvents: function() {
17262         
17263         if(!this.calevents){
17264             return;
17265         }
17266         
17267         Roo.each(this.cells.elements, function(c){
17268             c.row = 0;
17269             c.events = [];
17270             c.more = [];
17271         });
17272         
17273         Roo.each(this.calevents, function(e) {
17274             Roo.each(e.els, function(el) {
17275                 el.un('mouseenter' ,this.onEventEnter, this);
17276                 el.un('mouseleave' ,this.onEventLeave, this);
17277                 el.remove();
17278             },this);
17279         },this);
17280         
17281         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17282             e.remove();
17283         });
17284         
17285     },
17286     
17287     renderEvents: function()
17288     {   
17289         var _this = this;
17290         
17291         this.cells.each(function(c) {
17292             
17293             if(c.row < 5){
17294                 return;
17295             }
17296             
17297             var ev = c.events;
17298             
17299             var r = 4;
17300             if(c.row != c.events.length){
17301                 r = 4 - (4 - (c.row - c.events.length));
17302             }
17303             
17304             c.events = ev.slice(0, r);
17305             c.more = ev.slice(r);
17306             
17307             if(c.more.length && c.more.length == 1){
17308                 c.events.push(c.more.pop());
17309             }
17310             
17311             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17312             
17313         });
17314             
17315         this.cells.each(function(c) {
17316             
17317             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17318             
17319             
17320             for (var e = 0; e < c.events.length; e++){
17321                 var ev = c.events[e];
17322                 var rows = ev.rows;
17323                 
17324                 for(var i = 0; i < rows.length; i++) {
17325                 
17326                     // how many rows should it span..
17327
17328                     var  cfg = {
17329                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17330                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17331
17332                         unselectable : "on",
17333                         cn : [
17334                             {
17335                                 cls: 'fc-event-inner',
17336                                 cn : [
17337     //                                {
17338     //                                  tag:'span',
17339     //                                  cls: 'fc-event-time',
17340     //                                  html : cells.length > 1 ? '' : ev.time
17341     //                                },
17342                                     {
17343                                       tag:'span',
17344                                       cls: 'fc-event-title',
17345                                       html : String.format('{0}', ev.title)
17346                                     }
17347
17348
17349                                 ]
17350                             },
17351                             {
17352                                 cls: 'ui-resizable-handle ui-resizable-e',
17353                                 html : '&nbsp;&nbsp;&nbsp'
17354                             }
17355
17356                         ]
17357                     };
17358
17359                     if (i == 0) {
17360                         cfg.cls += ' fc-event-start';
17361                     }
17362                     if ((i+1) == rows.length) {
17363                         cfg.cls += ' fc-event-end';
17364                     }
17365
17366                     var ctr = _this.el.select('.fc-event-container',true).first();
17367                     var cg = ctr.createChild(cfg);
17368
17369                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17370                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17371
17372                     var r = (c.more.length) ? 1 : 0;
17373                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17374                     cg.setWidth(ebox.right - sbox.x -2);
17375
17376                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17377                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17378                     cg.on('click', _this.onEventClick, _this, ev);
17379
17380                     ev.els.push(cg);
17381                     
17382                 }
17383                 
17384             }
17385             
17386             
17387             if(c.more.length){
17388                 var  cfg = {
17389                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17390                     style : 'position: absolute',
17391                     unselectable : "on",
17392                     cn : [
17393                         {
17394                             cls: 'fc-event-inner',
17395                             cn : [
17396                                 {
17397                                   tag:'span',
17398                                   cls: 'fc-event-title',
17399                                   html : 'More'
17400                                 }
17401
17402
17403                             ]
17404                         },
17405                         {
17406                             cls: 'ui-resizable-handle ui-resizable-e',
17407                             html : '&nbsp;&nbsp;&nbsp'
17408                         }
17409
17410                     ]
17411                 };
17412
17413                 var ctr = _this.el.select('.fc-event-container',true).first();
17414                 var cg = ctr.createChild(cfg);
17415
17416                 var sbox = c.select('.fc-day-content',true).first().getBox();
17417                 var ebox = c.select('.fc-day-content',true).first().getBox();
17418                 //Roo.log(cg);
17419                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17420                 cg.setWidth(ebox.right - sbox.x -2);
17421
17422                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17423                 
17424             }
17425             
17426         });
17427         
17428         
17429         
17430     },
17431     
17432     onEventEnter: function (e, el,event,d) {
17433         this.fireEvent('evententer', this, el, event);
17434     },
17435     
17436     onEventLeave: function (e, el,event,d) {
17437         this.fireEvent('eventleave', this, el, event);
17438     },
17439     
17440     onEventClick: function (e, el,event,d) {
17441         this.fireEvent('eventclick', this, el, event);
17442     },
17443     
17444     onMonthChange: function () {
17445         this.store.load();
17446     },
17447     
17448     onMoreEventClick: function(e, el, more)
17449     {
17450         var _this = this;
17451         
17452         this.calpopover.placement = 'right';
17453         this.calpopover.setTitle('More');
17454         
17455         this.calpopover.setContent('');
17456         
17457         var ctr = this.calpopover.el.select('.popover-content', true).first();
17458         
17459         Roo.each(more, function(m){
17460             var cfg = {
17461                 cls : 'fc-event-hori fc-event-draggable',
17462                 html : m.title
17463             };
17464             var cg = ctr.createChild(cfg);
17465             
17466             cg.on('click', _this.onEventClick, _this, m);
17467         });
17468         
17469         this.calpopover.show(el);
17470         
17471         
17472     },
17473     
17474     onLoad: function () 
17475     {   
17476         this.calevents = [];
17477         var cal = this;
17478         
17479         if(this.store.getCount() > 0){
17480             this.store.data.each(function(d){
17481                cal.addItem({
17482                     id : d.data.id,
17483                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17484                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17485                     time : d.data.start_time,
17486                     title : d.data.title,
17487                     description : d.data.description,
17488                     venue : d.data.venue
17489                 });
17490             });
17491         }
17492         
17493         this.renderEvents();
17494         
17495         if(this.calevents.length && this.loadMask){
17496             this.maskEl.hide();
17497         }
17498     },
17499     
17500     onBeforeLoad: function()
17501     {
17502         this.clearEvents();
17503         if(this.loadMask){
17504             this.maskEl.show();
17505         }
17506     }
17507 });
17508
17509  
17510  /*
17511  * - LGPL
17512  *
17513  * element
17514  * 
17515  */
17516
17517 /**
17518  * @class Roo.bootstrap.Popover
17519  * @extends Roo.bootstrap.Component
17520  * Bootstrap Popover class
17521  * @cfg {String} html contents of the popover   (or false to use children..)
17522  * @cfg {String} title of popover (or false to hide)
17523  * @cfg {String} placement how it is placed
17524  * @cfg {String} trigger click || hover (or false to trigger manually)
17525  * @cfg {String} over what (parent or false to trigger manually.)
17526  * @cfg {Number} delay - delay before showing
17527  
17528  * @constructor
17529  * Create a new Popover
17530  * @param {Object} config The config object
17531  */
17532
17533 Roo.bootstrap.Popover = function(config){
17534     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17535     
17536     this.addEvents({
17537         // raw events
17538          /**
17539          * @event show
17540          * After the popover show
17541          * 
17542          * @param {Roo.bootstrap.Popover} this
17543          */
17544         "show" : true,
17545         /**
17546          * @event hide
17547          * After the popover hide
17548          * 
17549          * @param {Roo.bootstrap.Popover} this
17550          */
17551         "hide" : true
17552     });
17553 };
17554
17555 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17556     
17557     title: 'Fill in a title',
17558     html: false,
17559     
17560     placement : 'right',
17561     trigger : 'hover', // hover
17562     
17563     delay : 0,
17564     
17565     over: 'parent',
17566     
17567     can_build_overlaid : false,
17568     
17569     getChildContainer : function()
17570     {
17571         return this.el.select('.popover-content',true).first();
17572     },
17573     
17574     getAutoCreate : function(){
17575          
17576         var cfg = {
17577            cls : 'popover roo-dynamic',
17578            style: 'display:block',
17579            cn : [
17580                 {
17581                     cls : 'arrow'
17582                 },
17583                 {
17584                     cls : 'popover-inner',
17585                     cn : [
17586                         {
17587                             tag: 'h3',
17588                             cls: 'popover-title',
17589                             html : this.title
17590                         },
17591                         {
17592                             cls : 'popover-content',
17593                             html : this.html
17594                         }
17595                     ]
17596                     
17597                 }
17598            ]
17599         };
17600         
17601         return cfg;
17602     },
17603     setTitle: function(str)
17604     {
17605         this.title = str;
17606         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17607     },
17608     setContent: function(str)
17609     {
17610         this.html = str;
17611         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17612     },
17613     // as it get's added to the bottom of the page.
17614     onRender : function(ct, position)
17615     {
17616         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17617         if(!this.el){
17618             var cfg = Roo.apply({},  this.getAutoCreate());
17619             cfg.id = Roo.id();
17620             
17621             if (this.cls) {
17622                 cfg.cls += ' ' + this.cls;
17623             }
17624             if (this.style) {
17625                 cfg.style = this.style;
17626             }
17627             //Roo.log("adding to ");
17628             this.el = Roo.get(document.body).createChild(cfg, position);
17629 //            Roo.log(this.el);
17630         }
17631         this.initEvents();
17632     },
17633     
17634     initEvents : function()
17635     {
17636         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17637         this.el.enableDisplayMode('block');
17638         this.el.hide();
17639         if (this.over === false) {
17640             return; 
17641         }
17642         if (this.triggers === false) {
17643             return;
17644         }
17645         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17646         var triggers = this.trigger ? this.trigger.split(' ') : [];
17647         Roo.each(triggers, function(trigger) {
17648         
17649             if (trigger == 'click') {
17650                 on_el.on('click', this.toggle, this);
17651             } else if (trigger != 'manual') {
17652                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17653                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17654       
17655                 on_el.on(eventIn  ,this.enter, this);
17656                 on_el.on(eventOut, this.leave, this);
17657             }
17658         }, this);
17659         
17660     },
17661     
17662     
17663     // private
17664     timeout : null,
17665     hoverState : null,
17666     
17667     toggle : function () {
17668         this.hoverState == 'in' ? this.leave() : this.enter();
17669     },
17670     
17671     enter : function () {
17672         
17673         clearTimeout(this.timeout);
17674     
17675         this.hoverState = 'in';
17676     
17677         if (!this.delay || !this.delay.show) {
17678             this.show();
17679             return;
17680         }
17681         var _t = this;
17682         this.timeout = setTimeout(function () {
17683             if (_t.hoverState == 'in') {
17684                 _t.show();
17685             }
17686         }, this.delay.show)
17687     },
17688     
17689     leave : function() {
17690         clearTimeout(this.timeout);
17691     
17692         this.hoverState = 'out';
17693     
17694         if (!this.delay || !this.delay.hide) {
17695             this.hide();
17696             return;
17697         }
17698         var _t = this;
17699         this.timeout = setTimeout(function () {
17700             if (_t.hoverState == 'out') {
17701                 _t.hide();
17702             }
17703         }, this.delay.hide)
17704     },
17705     
17706     show : function (on_el)
17707     {
17708         if (!on_el) {
17709             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17710         }
17711         
17712         // set content.
17713         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17714         if (this.html !== false) {
17715             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17716         }
17717         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17718         if (!this.title.length) {
17719             this.el.select('.popover-title',true).hide();
17720         }
17721         
17722         var placement = typeof this.placement == 'function' ?
17723             this.placement.call(this, this.el, on_el) :
17724             this.placement;
17725             
17726         var autoToken = /\s?auto?\s?/i;
17727         var autoPlace = autoToken.test(placement);
17728         if (autoPlace) {
17729             placement = placement.replace(autoToken, '') || 'top';
17730         }
17731         
17732         //this.el.detach()
17733         //this.el.setXY([0,0]);
17734         this.el.show();
17735         this.el.dom.style.display='block';
17736         this.el.addClass(placement);
17737         
17738         //this.el.appendTo(on_el);
17739         
17740         var p = this.getPosition();
17741         var box = this.el.getBox();
17742         
17743         if (autoPlace) {
17744             // fixme..
17745         }
17746         var align = Roo.bootstrap.Popover.alignment[placement];
17747         
17748 //        Roo.log(align);
17749         this.el.alignTo(on_el, align[0],align[1]);
17750         //var arrow = this.el.select('.arrow',true).first();
17751         //arrow.set(align[2], 
17752         
17753         this.el.addClass('in');
17754         
17755         
17756         if (this.el.hasClass('fade')) {
17757             // fade it?
17758         }
17759         
17760         this.hoverState = 'in';
17761         
17762         this.fireEvent('show', this);
17763         
17764     },
17765     hide : function()
17766     {
17767         this.el.setXY([0,0]);
17768         this.el.removeClass('in');
17769         this.el.hide();
17770         this.hoverState = null;
17771         
17772         this.fireEvent('hide', this);
17773     }
17774     
17775 });
17776
17777 Roo.bootstrap.Popover.alignment = {
17778     'left' : ['r-l', [-10,0], 'right'],
17779     'right' : ['l-r', [10,0], 'left'],
17780     'bottom' : ['t-b', [0,10], 'top'],
17781     'top' : [ 'b-t', [0,-10], 'bottom']
17782 };
17783
17784  /*
17785  * - LGPL
17786  *
17787  * Progress
17788  * 
17789  */
17790
17791 /**
17792  * @class Roo.bootstrap.Progress
17793  * @extends Roo.bootstrap.Component
17794  * Bootstrap Progress class
17795  * @cfg {Boolean} striped striped of the progress bar
17796  * @cfg {Boolean} active animated of the progress bar
17797  * 
17798  * 
17799  * @constructor
17800  * Create a new Progress
17801  * @param {Object} config The config object
17802  */
17803
17804 Roo.bootstrap.Progress = function(config){
17805     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17806 };
17807
17808 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17809     
17810     striped : false,
17811     active: false,
17812     
17813     getAutoCreate : function(){
17814         var cfg = {
17815             tag: 'div',
17816             cls: 'progress'
17817         };
17818         
17819         
17820         if(this.striped){
17821             cfg.cls += ' progress-striped';
17822         }
17823       
17824         if(this.active){
17825             cfg.cls += ' active';
17826         }
17827         
17828         
17829         return cfg;
17830     }
17831    
17832 });
17833
17834  
17835
17836  /*
17837  * - LGPL
17838  *
17839  * ProgressBar
17840  * 
17841  */
17842
17843 /**
17844  * @class Roo.bootstrap.ProgressBar
17845  * @extends Roo.bootstrap.Component
17846  * Bootstrap ProgressBar class
17847  * @cfg {Number} aria_valuenow aria-value now
17848  * @cfg {Number} aria_valuemin aria-value min
17849  * @cfg {Number} aria_valuemax aria-value max
17850  * @cfg {String} label label for the progress bar
17851  * @cfg {String} panel (success | info | warning | danger )
17852  * @cfg {String} role role of the progress bar
17853  * @cfg {String} sr_only text
17854  * 
17855  * 
17856  * @constructor
17857  * Create a new ProgressBar
17858  * @param {Object} config The config object
17859  */
17860
17861 Roo.bootstrap.ProgressBar = function(config){
17862     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17863 };
17864
17865 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17866     
17867     aria_valuenow : 0,
17868     aria_valuemin : 0,
17869     aria_valuemax : 100,
17870     label : false,
17871     panel : false,
17872     role : false,
17873     sr_only: false,
17874     
17875     getAutoCreate : function()
17876     {
17877         
17878         var cfg = {
17879             tag: 'div',
17880             cls: 'progress-bar',
17881             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17882         };
17883         
17884         if(this.sr_only){
17885             cfg.cn = {
17886                 tag: 'span',
17887                 cls: 'sr-only',
17888                 html: this.sr_only
17889             }
17890         }
17891         
17892         if(this.role){
17893             cfg.role = this.role;
17894         }
17895         
17896         if(this.aria_valuenow){
17897             cfg['aria-valuenow'] = this.aria_valuenow;
17898         }
17899         
17900         if(this.aria_valuemin){
17901             cfg['aria-valuemin'] = this.aria_valuemin;
17902         }
17903         
17904         if(this.aria_valuemax){
17905             cfg['aria-valuemax'] = this.aria_valuemax;
17906         }
17907         
17908         if(this.label && !this.sr_only){
17909             cfg.html = this.label;
17910         }
17911         
17912         if(this.panel){
17913             cfg.cls += ' progress-bar-' + this.panel;
17914         }
17915         
17916         return cfg;
17917     },
17918     
17919     update : function(aria_valuenow)
17920     {
17921         this.aria_valuenow = aria_valuenow;
17922         
17923         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17924     }
17925    
17926 });
17927
17928  
17929
17930  /*
17931  * - LGPL
17932  *
17933  * column
17934  * 
17935  */
17936
17937 /**
17938  * @class Roo.bootstrap.TabGroup
17939  * @extends Roo.bootstrap.Column
17940  * Bootstrap Column class
17941  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17942  * @cfg {Boolean} carousel true to make the group behave like a carousel
17943  * @cfg {Boolean} bullets show bullets for the panels
17944  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17945  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17946  * @cfg {Boolean} showarrow (true|false) show arrow default true
17947  * 
17948  * @constructor
17949  * Create a new TabGroup
17950  * @param {Object} config The config object
17951  */
17952
17953 Roo.bootstrap.TabGroup = function(config){
17954     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17955     if (!this.navId) {
17956         this.navId = Roo.id();
17957     }
17958     this.tabs = [];
17959     Roo.bootstrap.TabGroup.register(this);
17960     
17961 };
17962
17963 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17964     
17965     carousel : false,
17966     transition : false,
17967     bullets : 0,
17968     timer : 0,
17969     autoslide : false,
17970     slideFn : false,
17971     slideOnTouch : false,
17972     showarrow : true,
17973     
17974     getAutoCreate : function()
17975     {
17976         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17977         
17978         cfg.cls += ' tab-content';
17979         
17980         if (this.carousel) {
17981             cfg.cls += ' carousel slide';
17982             
17983             cfg.cn = [{
17984                cls : 'carousel-inner',
17985                cn : []
17986             }];
17987         
17988             if(this.bullets  && !Roo.isTouch){
17989                 
17990                 var bullets = {
17991                     cls : 'carousel-bullets',
17992                     cn : []
17993                 };
17994                
17995                 if(this.bullets_cls){
17996                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17997                 }
17998                 
17999                 bullets.cn.push({
18000                     cls : 'clear'
18001                 });
18002                 
18003                 cfg.cn[0].cn.push(bullets);
18004             }
18005             
18006             if(this.showarrow){
18007                 cfg.cn[0].cn.push({
18008                     tag : 'div',
18009                     class : 'carousel-arrow',
18010                     cn : [
18011                         {
18012                             tag : 'div',
18013                             class : 'carousel-prev',
18014                             cn : [
18015                                 {
18016                                     tag : 'i',
18017                                     class : 'fa fa-chevron-left'
18018                                 }
18019                             ]
18020                         },
18021                         {
18022                             tag : 'div',
18023                             class : 'carousel-next',
18024                             cn : [
18025                                 {
18026                                     tag : 'i',
18027                                     class : 'fa fa-chevron-right'
18028                                 }
18029                             ]
18030                         }
18031                     ]
18032                 });
18033             }
18034             
18035         }
18036         
18037         return cfg;
18038     },
18039     
18040     initEvents:  function()
18041     {
18042 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18043 //            this.el.on("touchstart", this.onTouchStart, this);
18044 //        }
18045         
18046         if(this.autoslide){
18047             var _this = this;
18048             
18049             this.slideFn = window.setInterval(function() {
18050                 _this.showPanelNext();
18051             }, this.timer);
18052         }
18053         
18054         if(this.showarrow){
18055             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18056             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18057         }
18058         
18059         
18060     },
18061     
18062 //    onTouchStart : function(e, el, o)
18063 //    {
18064 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18065 //            return;
18066 //        }
18067 //        
18068 //        this.showPanelNext();
18069 //    },
18070     
18071     
18072     getChildContainer : function()
18073     {
18074         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18075     },
18076     
18077     /**
18078     * register a Navigation item
18079     * @param {Roo.bootstrap.NavItem} the navitem to add
18080     */
18081     register : function(item)
18082     {
18083         this.tabs.push( item);
18084         item.navId = this.navId; // not really needed..
18085         this.addBullet();
18086     
18087     },
18088     
18089     getActivePanel : function()
18090     {
18091         var r = false;
18092         Roo.each(this.tabs, function(t) {
18093             if (t.active) {
18094                 r = t;
18095                 return false;
18096             }
18097             return null;
18098         });
18099         return r;
18100         
18101     },
18102     getPanelByName : function(n)
18103     {
18104         var r = false;
18105         Roo.each(this.tabs, function(t) {
18106             if (t.tabId == n) {
18107                 r = t;
18108                 return false;
18109             }
18110             return null;
18111         });
18112         return r;
18113     },
18114     indexOfPanel : function(p)
18115     {
18116         var r = false;
18117         Roo.each(this.tabs, function(t,i) {
18118             if (t.tabId == p.tabId) {
18119                 r = i;
18120                 return false;
18121             }
18122             return null;
18123         });
18124         return r;
18125     },
18126     /**
18127      * show a specific panel
18128      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18129      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18130      */
18131     showPanel : function (pan)
18132     {
18133         if(this.transition || typeof(pan) == 'undefined'){
18134             Roo.log("waiting for the transitionend");
18135             return;
18136         }
18137         
18138         if (typeof(pan) == 'number') {
18139             pan = this.tabs[pan];
18140         }
18141         
18142         if (typeof(pan) == 'string') {
18143             pan = this.getPanelByName(pan);
18144         }
18145         
18146         var cur = this.getActivePanel();
18147         
18148         if(!pan || !cur){
18149             Roo.log('pan or acitve pan is undefined');
18150             return false;
18151         }
18152         
18153         if (pan.tabId == this.getActivePanel().tabId) {
18154             return true;
18155         }
18156         
18157         if (false === cur.fireEvent('beforedeactivate')) {
18158             return false;
18159         }
18160         
18161         if(this.bullets > 0 && !Roo.isTouch){
18162             this.setActiveBullet(this.indexOfPanel(pan));
18163         }
18164         
18165         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18166             
18167             this.transition = true;
18168             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18169             var lr = dir == 'next' ? 'left' : 'right';
18170             pan.el.addClass(dir); // or prev
18171             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18172             cur.el.addClass(lr); // or right
18173             pan.el.addClass(lr);
18174             
18175             var _this = this;
18176             cur.el.on('transitionend', function() {
18177                 Roo.log("trans end?");
18178                 
18179                 pan.el.removeClass([lr,dir]);
18180                 pan.setActive(true);
18181                 
18182                 cur.el.removeClass([lr]);
18183                 cur.setActive(false);
18184                 
18185                 _this.transition = false;
18186                 
18187             }, this, { single:  true } );
18188             
18189             return true;
18190         }
18191         
18192         cur.setActive(false);
18193         pan.setActive(true);
18194         
18195         return true;
18196         
18197     },
18198     showPanelNext : function()
18199     {
18200         var i = this.indexOfPanel(this.getActivePanel());
18201         
18202         if (i >= this.tabs.length - 1 && !this.autoslide) {
18203             return;
18204         }
18205         
18206         if (i >= this.tabs.length - 1 && this.autoslide) {
18207             i = -1;
18208         }
18209         
18210         this.showPanel(this.tabs[i+1]);
18211     },
18212     
18213     showPanelPrev : function()
18214     {
18215         var i = this.indexOfPanel(this.getActivePanel());
18216         
18217         if (i  < 1 && !this.autoslide) {
18218             return;
18219         }
18220         
18221         if (i < 1 && this.autoslide) {
18222             i = this.tabs.length;
18223         }
18224         
18225         this.showPanel(this.tabs[i-1]);
18226     },
18227     
18228     
18229     addBullet: function()
18230     {
18231         if(!this.bullets || Roo.isTouch){
18232             return;
18233         }
18234         var ctr = this.el.select('.carousel-bullets',true).first();
18235         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18236         var bullet = ctr.createChild({
18237             cls : 'bullet bullet-' + i
18238         },ctr.dom.lastChild);
18239         
18240         
18241         var _this = this;
18242         
18243         bullet.on('click', (function(e, el, o, ii, t){
18244
18245             e.preventDefault();
18246
18247             this.showPanel(ii);
18248
18249             if(this.autoslide && this.slideFn){
18250                 clearInterval(this.slideFn);
18251                 this.slideFn = window.setInterval(function() {
18252                     _this.showPanelNext();
18253                 }, this.timer);
18254             }
18255
18256         }).createDelegate(this, [i, bullet], true));
18257                 
18258         
18259     },
18260      
18261     setActiveBullet : function(i)
18262     {
18263         if(Roo.isTouch){
18264             return;
18265         }
18266         
18267         Roo.each(this.el.select('.bullet', true).elements, function(el){
18268             el.removeClass('selected');
18269         });
18270
18271         var bullet = this.el.select('.bullet-' + i, true).first();
18272         
18273         if(!bullet){
18274             return;
18275         }
18276         
18277         bullet.addClass('selected');
18278     }
18279     
18280     
18281   
18282 });
18283
18284  
18285
18286  
18287  
18288 Roo.apply(Roo.bootstrap.TabGroup, {
18289     
18290     groups: {},
18291      /**
18292     * register a Navigation Group
18293     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18294     */
18295     register : function(navgrp)
18296     {
18297         this.groups[navgrp.navId] = navgrp;
18298         
18299     },
18300     /**
18301     * fetch a Navigation Group based on the navigation ID
18302     * if one does not exist , it will get created.
18303     * @param {string} the navgroup to add
18304     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18305     */
18306     get: function(navId) {
18307         if (typeof(this.groups[navId]) == 'undefined') {
18308             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18309         }
18310         return this.groups[navId] ;
18311     }
18312     
18313     
18314     
18315 });
18316
18317  /*
18318  * - LGPL
18319  *
18320  * TabPanel
18321  * 
18322  */
18323
18324 /**
18325  * @class Roo.bootstrap.TabPanel
18326  * @extends Roo.bootstrap.Component
18327  * Bootstrap TabPanel class
18328  * @cfg {Boolean} active panel active
18329  * @cfg {String} html panel content
18330  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18331  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18332  * @cfg {String} href click to link..
18333  * 
18334  * 
18335  * @constructor
18336  * Create a new TabPanel
18337  * @param {Object} config The config object
18338  */
18339
18340 Roo.bootstrap.TabPanel = function(config){
18341     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18342     this.addEvents({
18343         /**
18344              * @event changed
18345              * Fires when the active status changes
18346              * @param {Roo.bootstrap.TabPanel} this
18347              * @param {Boolean} state the new state
18348             
18349          */
18350         'changed': true,
18351         /**
18352              * @event beforedeactivate
18353              * Fires before a tab is de-activated - can be used to do validation on a form.
18354              * @param {Roo.bootstrap.TabPanel} this
18355              * @return {Boolean} false if there is an error
18356             
18357          */
18358         'beforedeactivate': true
18359      });
18360     
18361     this.tabId = this.tabId || Roo.id();
18362   
18363 };
18364
18365 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18366     
18367     active: false,
18368     html: false,
18369     tabId: false,
18370     navId : false,
18371     href : '',
18372     
18373     getAutoCreate : function(){
18374         var cfg = {
18375             tag: 'div',
18376             // item is needed for carousel - not sure if it has any effect otherwise
18377             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18378             html: this.html || ''
18379         };
18380         
18381         if(this.active){
18382             cfg.cls += ' active';
18383         }
18384         
18385         if(this.tabId){
18386             cfg.tabId = this.tabId;
18387         }
18388         
18389         
18390         return cfg;
18391     },
18392     
18393     initEvents:  function()
18394     {
18395         var p = this.parent();
18396         
18397         this.navId = this.navId || p.navId;
18398         
18399         if (typeof(this.navId) != 'undefined') {
18400             // not really needed.. but just in case.. parent should be a NavGroup.
18401             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18402             
18403             tg.register(this);
18404             
18405             var i = tg.tabs.length - 1;
18406             
18407             if(this.active && tg.bullets > 0 && i < tg.bullets){
18408                 tg.setActiveBullet(i);
18409             }
18410         }
18411         
18412         this.el.on('click', this.onClick, this);
18413         
18414         if(Roo.isTouch){
18415             this.el.on("touchstart", this.onTouchStart, this);
18416             this.el.on("touchmove", this.onTouchMove, this);
18417             this.el.on("touchend", this.onTouchEnd, this);
18418         }
18419         
18420     },
18421     
18422     onRender : function(ct, position)
18423     {
18424         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18425     },
18426     
18427     setActive : function(state)
18428     {
18429         Roo.log("panel - set active " + this.tabId + "=" + state);
18430         
18431         this.active = state;
18432         if (!state) {
18433             this.el.removeClass('active');
18434             
18435         } else  if (!this.el.hasClass('active')) {
18436             this.el.addClass('active');
18437         }
18438         
18439         this.fireEvent('changed', this, state);
18440     },
18441     
18442     onClick : function(e)
18443     {
18444         e.preventDefault();
18445         
18446         if(!this.href.length){
18447             return;
18448         }
18449         
18450         window.location.href = this.href;
18451     },
18452     
18453     startX : 0,
18454     startY : 0,
18455     endX : 0,
18456     endY : 0,
18457     swiping : false,
18458     
18459     onTouchStart : function(e)
18460     {
18461         this.swiping = false;
18462         
18463         this.startX = e.browserEvent.touches[0].clientX;
18464         this.startY = e.browserEvent.touches[0].clientY;
18465     },
18466     
18467     onTouchMove : function(e)
18468     {
18469         this.swiping = true;
18470         
18471         this.endX = e.browserEvent.touches[0].clientX;
18472         this.endY = e.browserEvent.touches[0].clientY;
18473     },
18474     
18475     onTouchEnd : function(e)
18476     {
18477         if(!this.swiping){
18478             this.onClick(e);
18479             return;
18480         }
18481         
18482         var tabGroup = this.parent();
18483         
18484         if(this.endX > this.startX){ // swiping right
18485             tabGroup.showPanelPrev();
18486             return;
18487         }
18488         
18489         if(this.startX > this.endX){ // swiping left
18490             tabGroup.showPanelNext();
18491             return;
18492         }
18493     }
18494     
18495     
18496 });
18497  
18498
18499  
18500
18501  /*
18502  * - LGPL
18503  *
18504  * DateField
18505  * 
18506  */
18507
18508 /**
18509  * @class Roo.bootstrap.DateField
18510  * @extends Roo.bootstrap.Input
18511  * Bootstrap DateField class
18512  * @cfg {Number} weekStart default 0
18513  * @cfg {String} viewMode default empty, (months|years)
18514  * @cfg {String} minViewMode default empty, (months|years)
18515  * @cfg {Number} startDate default -Infinity
18516  * @cfg {Number} endDate default Infinity
18517  * @cfg {Boolean} todayHighlight default false
18518  * @cfg {Boolean} todayBtn default false
18519  * @cfg {Boolean} calendarWeeks default false
18520  * @cfg {Object} daysOfWeekDisabled default empty
18521  * @cfg {Boolean} singleMode default false (true | false)
18522  * 
18523  * @cfg {Boolean} keyboardNavigation default true
18524  * @cfg {String} language default en
18525  * 
18526  * @constructor
18527  * Create a new DateField
18528  * @param {Object} config The config object
18529  */
18530
18531 Roo.bootstrap.DateField = function(config){
18532     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18533      this.addEvents({
18534             /**
18535              * @event show
18536              * Fires when this field show.
18537              * @param {Roo.bootstrap.DateField} this
18538              * @param {Mixed} date The date value
18539              */
18540             show : true,
18541             /**
18542              * @event show
18543              * Fires when this field hide.
18544              * @param {Roo.bootstrap.DateField} this
18545              * @param {Mixed} date The date value
18546              */
18547             hide : true,
18548             /**
18549              * @event select
18550              * Fires when select a date.
18551              * @param {Roo.bootstrap.DateField} this
18552              * @param {Mixed} date The date value
18553              */
18554             select : true,
18555             /**
18556              * @event beforeselect
18557              * Fires when before select a date.
18558              * @param {Roo.bootstrap.DateField} this
18559              * @param {Mixed} date The date value
18560              */
18561             beforeselect : true
18562         });
18563 };
18564
18565 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18566     
18567     /**
18568      * @cfg {String} format
18569      * The default date format string which can be overriden for localization support.  The format must be
18570      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18571      */
18572     format : "m/d/y",
18573     /**
18574      * @cfg {String} altFormats
18575      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18576      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18577      */
18578     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18579     
18580     weekStart : 0,
18581     
18582     viewMode : '',
18583     
18584     minViewMode : '',
18585     
18586     todayHighlight : false,
18587     
18588     todayBtn: false,
18589     
18590     language: 'en',
18591     
18592     keyboardNavigation: true,
18593     
18594     calendarWeeks: false,
18595     
18596     startDate: -Infinity,
18597     
18598     endDate: Infinity,
18599     
18600     daysOfWeekDisabled: [],
18601     
18602     _events: [],
18603     
18604     singleMode : false,
18605     
18606     UTCDate: function()
18607     {
18608         return new Date(Date.UTC.apply(Date, arguments));
18609     },
18610     
18611     UTCToday: function()
18612     {
18613         var today = new Date();
18614         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18615     },
18616     
18617     getDate: function() {
18618             var d = this.getUTCDate();
18619             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18620     },
18621     
18622     getUTCDate: function() {
18623             return this.date;
18624     },
18625     
18626     setDate: function(d) {
18627             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18628     },
18629     
18630     setUTCDate: function(d) {
18631             this.date = d;
18632             this.setValue(this.formatDate(this.date));
18633     },
18634         
18635     onRender: function(ct, position)
18636     {
18637         
18638         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18639         
18640         this.language = this.language || 'en';
18641         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18642         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18643         
18644         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18645         this.format = this.format || 'm/d/y';
18646         this.isInline = false;
18647         this.isInput = true;
18648         this.component = this.el.select('.add-on', true).first() || false;
18649         this.component = (this.component && this.component.length === 0) ? false : this.component;
18650         this.hasInput = this.component && this.inputEl().length;
18651         
18652         if (typeof(this.minViewMode === 'string')) {
18653             switch (this.minViewMode) {
18654                 case 'months':
18655                     this.minViewMode = 1;
18656                     break;
18657                 case 'years':
18658                     this.minViewMode = 2;
18659                     break;
18660                 default:
18661                     this.minViewMode = 0;
18662                     break;
18663             }
18664         }
18665         
18666         if (typeof(this.viewMode === 'string')) {
18667             switch (this.viewMode) {
18668                 case 'months':
18669                     this.viewMode = 1;
18670                     break;
18671                 case 'years':
18672                     this.viewMode = 2;
18673                     break;
18674                 default:
18675                     this.viewMode = 0;
18676                     break;
18677             }
18678         }
18679                 
18680         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18681         
18682 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18683         
18684         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18685         
18686         this.picker().on('mousedown', this.onMousedown, this);
18687         this.picker().on('click', this.onClick, this);
18688         
18689         this.picker().addClass('datepicker-dropdown');
18690         
18691         this.startViewMode = this.viewMode;
18692         
18693         if(this.singleMode){
18694             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18695                 v.setVisibilityMode(Roo.Element.DISPLAY);
18696                 v.hide();
18697             });
18698             
18699             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18700                 v.setStyle('width', '189px');
18701             });
18702         }
18703         
18704         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18705             if(!this.calendarWeeks){
18706                 v.remove();
18707                 return;
18708             }
18709             
18710             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18711             v.attr('colspan', function(i, val){
18712                 return parseInt(val) + 1;
18713             });
18714         });
18715                         
18716         
18717         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18718         
18719         this.setStartDate(this.startDate);
18720         this.setEndDate(this.endDate);
18721         
18722         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18723         
18724         this.fillDow();
18725         this.fillMonths();
18726         this.update();
18727         this.showMode();
18728         
18729         if(this.isInline) {
18730             this.showPopup();
18731         }
18732     },
18733     
18734     picker : function()
18735     {
18736         return this.pickerEl;
18737 //        return this.el.select('.datepicker', true).first();
18738     },
18739     
18740     fillDow: function()
18741     {
18742         var dowCnt = this.weekStart;
18743         
18744         var dow = {
18745             tag: 'tr',
18746             cn: [
18747                 
18748             ]
18749         };
18750         
18751         if(this.calendarWeeks){
18752             dow.cn.push({
18753                 tag: 'th',
18754                 cls: 'cw',
18755                 html: '&nbsp;'
18756             })
18757         }
18758         
18759         while (dowCnt < this.weekStart + 7) {
18760             dow.cn.push({
18761                 tag: 'th',
18762                 cls: 'dow',
18763                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18764             });
18765         }
18766         
18767         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18768     },
18769     
18770     fillMonths: function()
18771     {    
18772         var i = 0;
18773         var months = this.picker().select('>.datepicker-months td', true).first();
18774         
18775         months.dom.innerHTML = '';
18776         
18777         while (i < 12) {
18778             var month = {
18779                 tag: 'span',
18780                 cls: 'month',
18781                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18782             };
18783             
18784             months.createChild(month);
18785         }
18786         
18787     },
18788     
18789     update: function()
18790     {
18791         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;
18792         
18793         if (this.date < this.startDate) {
18794             this.viewDate = new Date(this.startDate);
18795         } else if (this.date > this.endDate) {
18796             this.viewDate = new Date(this.endDate);
18797         } else {
18798             this.viewDate = new Date(this.date);
18799         }
18800         
18801         this.fill();
18802     },
18803     
18804     fill: function() 
18805     {
18806         var d = new Date(this.viewDate),
18807                 year = d.getUTCFullYear(),
18808                 month = d.getUTCMonth(),
18809                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18810                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18811                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18812                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18813                 currentDate = this.date && this.date.valueOf(),
18814                 today = this.UTCToday();
18815         
18816         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18817         
18818 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18819         
18820 //        this.picker.select('>tfoot th.today').
18821 //                                              .text(dates[this.language].today)
18822 //                                              .toggle(this.todayBtn !== false);
18823     
18824         this.updateNavArrows();
18825         this.fillMonths();
18826                                                 
18827         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18828         
18829         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18830          
18831         prevMonth.setUTCDate(day);
18832         
18833         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18834         
18835         var nextMonth = new Date(prevMonth);
18836         
18837         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18838         
18839         nextMonth = nextMonth.valueOf();
18840         
18841         var fillMonths = false;
18842         
18843         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18844         
18845         while(prevMonth.valueOf() <= nextMonth) {
18846             var clsName = '';
18847             
18848             if (prevMonth.getUTCDay() === this.weekStart) {
18849                 if(fillMonths){
18850                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18851                 }
18852                     
18853                 fillMonths = {
18854                     tag: 'tr',
18855                     cn: []
18856                 };
18857                 
18858                 if(this.calendarWeeks){
18859                     // ISO 8601: First week contains first thursday.
18860                     // ISO also states week starts on Monday, but we can be more abstract here.
18861                     var
18862                     // Start of current week: based on weekstart/current date
18863                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18864                     // Thursday of this week
18865                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18866                     // First Thursday of year, year from thursday
18867                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18868                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18869                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18870                     
18871                     fillMonths.cn.push({
18872                         tag: 'td',
18873                         cls: 'cw',
18874                         html: calWeek
18875                     });
18876                 }
18877             }
18878             
18879             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18880                 clsName += ' old';
18881             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18882                 clsName += ' new';
18883             }
18884             if (this.todayHighlight &&
18885                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18886                 prevMonth.getUTCMonth() == today.getMonth() &&
18887                 prevMonth.getUTCDate() == today.getDate()) {
18888                 clsName += ' today';
18889             }
18890             
18891             if (currentDate && prevMonth.valueOf() === currentDate) {
18892                 clsName += ' active';
18893             }
18894             
18895             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18896                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18897                     clsName += ' disabled';
18898             }
18899             
18900             fillMonths.cn.push({
18901                 tag: 'td',
18902                 cls: 'day ' + clsName,
18903                 html: prevMonth.getDate()
18904             });
18905             
18906             prevMonth.setDate(prevMonth.getDate()+1);
18907         }
18908           
18909         var currentYear = this.date && this.date.getUTCFullYear();
18910         var currentMonth = this.date && this.date.getUTCMonth();
18911         
18912         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18913         
18914         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18915             v.removeClass('active');
18916             
18917             if(currentYear === year && k === currentMonth){
18918                 v.addClass('active');
18919             }
18920             
18921             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18922                 v.addClass('disabled');
18923             }
18924             
18925         });
18926         
18927         
18928         year = parseInt(year/10, 10) * 10;
18929         
18930         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18931         
18932         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18933         
18934         year -= 1;
18935         for (var i = -1; i < 11; i++) {
18936             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18937                 tag: 'span',
18938                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18939                 html: year
18940             });
18941             
18942             year += 1;
18943         }
18944     },
18945     
18946     showMode: function(dir) 
18947     {
18948         if (dir) {
18949             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18950         }
18951         
18952         Roo.each(this.picker().select('>div',true).elements, function(v){
18953             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18954             v.hide();
18955         });
18956         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18957     },
18958     
18959     place: function()
18960     {
18961         if(this.isInline) {
18962             return;
18963         }
18964         
18965         this.picker().removeClass(['bottom', 'top']);
18966         
18967         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18968             /*
18969              * place to the top of element!
18970              *
18971              */
18972             
18973             this.picker().addClass('top');
18974             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18975             
18976             return;
18977         }
18978         
18979         this.picker().addClass('bottom');
18980         
18981         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18982     },
18983     
18984     parseDate : function(value)
18985     {
18986         if(!value || value instanceof Date){
18987             return value;
18988         }
18989         var v = Date.parseDate(value, this.format);
18990         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18991             v = Date.parseDate(value, 'Y-m-d');
18992         }
18993         if(!v && this.altFormats){
18994             if(!this.altFormatsArray){
18995                 this.altFormatsArray = this.altFormats.split("|");
18996             }
18997             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18998                 v = Date.parseDate(value, this.altFormatsArray[i]);
18999             }
19000         }
19001         return v;
19002     },
19003     
19004     formatDate : function(date, fmt)
19005     {   
19006         return (!date || !(date instanceof Date)) ?
19007         date : date.dateFormat(fmt || this.format);
19008     },
19009     
19010     onFocus : function()
19011     {
19012         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19013         this.showPopup();
19014     },
19015     
19016     onBlur : function()
19017     {
19018         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19019         
19020         var d = this.inputEl().getValue();
19021         
19022         this.setValue(d);
19023                 
19024         this.hidePopup();
19025     },
19026     
19027     showPopup : function()
19028     {
19029         this.picker().show();
19030         this.update();
19031         this.place();
19032         
19033         this.fireEvent('showpopup', this, this.date);
19034     },
19035     
19036     hidePopup : function()
19037     {
19038         if(this.isInline) {
19039             return;
19040         }
19041         this.picker().hide();
19042         this.viewMode = this.startViewMode;
19043         this.showMode();
19044         
19045         this.fireEvent('hidepopup', this, this.date);
19046         
19047     },
19048     
19049     onMousedown: function(e)
19050     {
19051         e.stopPropagation();
19052         e.preventDefault();
19053     },
19054     
19055     keyup: function(e)
19056     {
19057         Roo.bootstrap.DateField.superclass.keyup.call(this);
19058         this.update();
19059     },
19060
19061     setValue: function(v)
19062     {
19063         if(this.fireEvent('beforeselect', this, v) !== false){
19064             var d = new Date(this.parseDate(v) ).clearTime();
19065         
19066             if(isNaN(d.getTime())){
19067                 this.date = this.viewDate = '';
19068                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19069                 return;
19070             }
19071
19072             v = this.formatDate(d);
19073
19074             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19075
19076             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19077
19078             this.update();
19079
19080             this.fireEvent('select', this, this.date);
19081         }
19082     },
19083     
19084     getValue: function()
19085     {
19086         return this.formatDate(this.date);
19087     },
19088     
19089     fireKey: function(e)
19090     {
19091         if (!this.picker().isVisible()){
19092             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19093                 this.showPopup();
19094             }
19095             return;
19096         }
19097         
19098         var dateChanged = false,
19099         dir, day, month,
19100         newDate, newViewDate;
19101         
19102         switch(e.keyCode){
19103             case 27: // escape
19104                 this.hidePopup();
19105                 e.preventDefault();
19106                 break;
19107             case 37: // left
19108             case 39: // right
19109                 if (!this.keyboardNavigation) {
19110                     break;
19111                 }
19112                 dir = e.keyCode == 37 ? -1 : 1;
19113                 
19114                 if (e.ctrlKey){
19115                     newDate = this.moveYear(this.date, dir);
19116                     newViewDate = this.moveYear(this.viewDate, dir);
19117                 } else if (e.shiftKey){
19118                     newDate = this.moveMonth(this.date, dir);
19119                     newViewDate = this.moveMonth(this.viewDate, dir);
19120                 } else {
19121                     newDate = new Date(this.date);
19122                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19123                     newViewDate = new Date(this.viewDate);
19124                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19125                 }
19126                 if (this.dateWithinRange(newDate)){
19127                     this.date = newDate;
19128                     this.viewDate = newViewDate;
19129                     this.setValue(this.formatDate(this.date));
19130 //                    this.update();
19131                     e.preventDefault();
19132                     dateChanged = true;
19133                 }
19134                 break;
19135             case 38: // up
19136             case 40: // down
19137                 if (!this.keyboardNavigation) {
19138                     break;
19139                 }
19140                 dir = e.keyCode == 38 ? -1 : 1;
19141                 if (e.ctrlKey){
19142                     newDate = this.moveYear(this.date, dir);
19143                     newViewDate = this.moveYear(this.viewDate, dir);
19144                 } else if (e.shiftKey){
19145                     newDate = this.moveMonth(this.date, dir);
19146                     newViewDate = this.moveMonth(this.viewDate, dir);
19147                 } else {
19148                     newDate = new Date(this.date);
19149                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19150                     newViewDate = new Date(this.viewDate);
19151                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19152                 }
19153                 if (this.dateWithinRange(newDate)){
19154                     this.date = newDate;
19155                     this.viewDate = newViewDate;
19156                     this.setValue(this.formatDate(this.date));
19157 //                    this.update();
19158                     e.preventDefault();
19159                     dateChanged = true;
19160                 }
19161                 break;
19162             case 13: // enter
19163                 this.setValue(this.formatDate(this.date));
19164                 this.hidePopup();
19165                 e.preventDefault();
19166                 break;
19167             case 9: // tab
19168                 this.setValue(this.formatDate(this.date));
19169                 this.hidePopup();
19170                 break;
19171             case 16: // shift
19172             case 17: // ctrl
19173             case 18: // alt
19174                 break;
19175             default :
19176                 this.hidePopup();
19177                 
19178         }
19179     },
19180     
19181     
19182     onClick: function(e) 
19183     {
19184         e.stopPropagation();
19185         e.preventDefault();
19186         
19187         var target = e.getTarget();
19188         
19189         if(target.nodeName.toLowerCase() === 'i'){
19190             target = Roo.get(target).dom.parentNode;
19191         }
19192         
19193         var nodeName = target.nodeName;
19194         var className = target.className;
19195         var html = target.innerHTML;
19196         //Roo.log(nodeName);
19197         
19198         switch(nodeName.toLowerCase()) {
19199             case 'th':
19200                 switch(className) {
19201                     case 'switch':
19202                         this.showMode(1);
19203                         break;
19204                     case 'prev':
19205                     case 'next':
19206                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19207                         switch(this.viewMode){
19208                                 case 0:
19209                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19210                                         break;
19211                                 case 1:
19212                                 case 2:
19213                                         this.viewDate = this.moveYear(this.viewDate, dir);
19214                                         break;
19215                         }
19216                         this.fill();
19217                         break;
19218                     case 'today':
19219                         var date = new Date();
19220                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19221 //                        this.fill()
19222                         this.setValue(this.formatDate(this.date));
19223                         
19224                         this.hidePopup();
19225                         break;
19226                 }
19227                 break;
19228             case 'span':
19229                 if (className.indexOf('disabled') < 0) {
19230                     this.viewDate.setUTCDate(1);
19231                     if (className.indexOf('month') > -1) {
19232                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19233                     } else {
19234                         var year = parseInt(html, 10) || 0;
19235                         this.viewDate.setUTCFullYear(year);
19236                         
19237                     }
19238                     
19239                     if(this.singleMode){
19240                         this.setValue(this.formatDate(this.viewDate));
19241                         this.hidePopup();
19242                         return;
19243                     }
19244                     
19245                     this.showMode(-1);
19246                     this.fill();
19247                 }
19248                 break;
19249                 
19250             case 'td':
19251                 //Roo.log(className);
19252                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19253                     var day = parseInt(html, 10) || 1;
19254                     var year = this.viewDate.getUTCFullYear(),
19255                         month = this.viewDate.getUTCMonth();
19256
19257                     if (className.indexOf('old') > -1) {
19258                         if(month === 0 ){
19259                             month = 11;
19260                             year -= 1;
19261                         }else{
19262                             month -= 1;
19263                         }
19264                     } else if (className.indexOf('new') > -1) {
19265                         if (month == 11) {
19266                             month = 0;
19267                             year += 1;
19268                         } else {
19269                             month += 1;
19270                         }
19271                     }
19272                     //Roo.log([year,month,day]);
19273                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19274                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19275 //                    this.fill();
19276                     //Roo.log(this.formatDate(this.date));
19277                     this.setValue(this.formatDate(this.date));
19278                     this.hidePopup();
19279                 }
19280                 break;
19281         }
19282     },
19283     
19284     setStartDate: function(startDate)
19285     {
19286         this.startDate = startDate || -Infinity;
19287         if (this.startDate !== -Infinity) {
19288             this.startDate = this.parseDate(this.startDate);
19289         }
19290         this.update();
19291         this.updateNavArrows();
19292     },
19293
19294     setEndDate: function(endDate)
19295     {
19296         this.endDate = endDate || Infinity;
19297         if (this.endDate !== Infinity) {
19298             this.endDate = this.parseDate(this.endDate);
19299         }
19300         this.update();
19301         this.updateNavArrows();
19302     },
19303     
19304     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19305     {
19306         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19307         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19308             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19309         }
19310         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19311             return parseInt(d, 10);
19312         });
19313         this.update();
19314         this.updateNavArrows();
19315     },
19316     
19317     updateNavArrows: function() 
19318     {
19319         if(this.singleMode){
19320             return;
19321         }
19322         
19323         var d = new Date(this.viewDate),
19324         year = d.getUTCFullYear(),
19325         month = d.getUTCMonth();
19326         
19327         Roo.each(this.picker().select('.prev', true).elements, function(v){
19328             v.show();
19329             switch (this.viewMode) {
19330                 case 0:
19331
19332                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19333                         v.hide();
19334                     }
19335                     break;
19336                 case 1:
19337                 case 2:
19338                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19339                         v.hide();
19340                     }
19341                     break;
19342             }
19343         });
19344         
19345         Roo.each(this.picker().select('.next', true).elements, function(v){
19346             v.show();
19347             switch (this.viewMode) {
19348                 case 0:
19349
19350                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19351                         v.hide();
19352                     }
19353                     break;
19354                 case 1:
19355                 case 2:
19356                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19357                         v.hide();
19358                     }
19359                     break;
19360             }
19361         })
19362     },
19363     
19364     moveMonth: function(date, dir)
19365     {
19366         if (!dir) {
19367             return date;
19368         }
19369         var new_date = new Date(date.valueOf()),
19370         day = new_date.getUTCDate(),
19371         month = new_date.getUTCMonth(),
19372         mag = Math.abs(dir),
19373         new_month, test;
19374         dir = dir > 0 ? 1 : -1;
19375         if (mag == 1){
19376             test = dir == -1
19377             // If going back one month, make sure month is not current month
19378             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19379             ? function(){
19380                 return new_date.getUTCMonth() == month;
19381             }
19382             // If going forward one month, make sure month is as expected
19383             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19384             : function(){
19385                 return new_date.getUTCMonth() != new_month;
19386             };
19387             new_month = month + dir;
19388             new_date.setUTCMonth(new_month);
19389             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19390             if (new_month < 0 || new_month > 11) {
19391                 new_month = (new_month + 12) % 12;
19392             }
19393         } else {
19394             // For magnitudes >1, move one month at a time...
19395             for (var i=0; i<mag; i++) {
19396                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19397                 new_date = this.moveMonth(new_date, dir);
19398             }
19399             // ...then reset the day, keeping it in the new month
19400             new_month = new_date.getUTCMonth();
19401             new_date.setUTCDate(day);
19402             test = function(){
19403                 return new_month != new_date.getUTCMonth();
19404             };
19405         }
19406         // Common date-resetting loop -- if date is beyond end of month, make it
19407         // end of month
19408         while (test()){
19409             new_date.setUTCDate(--day);
19410             new_date.setUTCMonth(new_month);
19411         }
19412         return new_date;
19413     },
19414
19415     moveYear: function(date, dir)
19416     {
19417         return this.moveMonth(date, dir*12);
19418     },
19419
19420     dateWithinRange: function(date)
19421     {
19422         return date >= this.startDate && date <= this.endDate;
19423     },
19424
19425     
19426     remove: function() 
19427     {
19428         this.picker().remove();
19429     },
19430     
19431     validateValue : function(value)
19432     {
19433         if(this.getVisibilityEl().hasClass('hidden')){
19434             return true;
19435         }
19436         
19437         if(value.length < 1)  {
19438             if(this.allowBlank){
19439                 return true;
19440             }
19441             return false;
19442         }
19443         
19444         if(value.length < this.minLength){
19445             return false;
19446         }
19447         if(value.length > this.maxLength){
19448             return false;
19449         }
19450         if(this.vtype){
19451             var vt = Roo.form.VTypes;
19452             if(!vt[this.vtype](value, this)){
19453                 return false;
19454             }
19455         }
19456         if(typeof this.validator == "function"){
19457             var msg = this.validator(value);
19458             if(msg !== true){
19459                 return false;
19460             }
19461         }
19462         
19463         if(this.regex && !this.regex.test(value)){
19464             return false;
19465         }
19466         
19467         if(typeof(this.parseDate(value)) == 'undefined'){
19468             return false;
19469         }
19470         
19471         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19472             return false;
19473         }      
19474         
19475         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19476             return false;
19477         } 
19478         
19479         
19480         return true;
19481     },
19482     
19483     reset : function()
19484     {
19485         this.date = this.viewDate = '';
19486         
19487         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19488     }
19489    
19490 });
19491
19492 Roo.apply(Roo.bootstrap.DateField,  {
19493     
19494     head : {
19495         tag: 'thead',
19496         cn: [
19497         {
19498             tag: 'tr',
19499             cn: [
19500             {
19501                 tag: 'th',
19502                 cls: 'prev',
19503                 html: '<i class="fa fa-arrow-left"/>'
19504             },
19505             {
19506                 tag: 'th',
19507                 cls: 'switch',
19508                 colspan: '5'
19509             },
19510             {
19511                 tag: 'th',
19512                 cls: 'next',
19513                 html: '<i class="fa fa-arrow-right"/>'
19514             }
19515
19516             ]
19517         }
19518         ]
19519     },
19520     
19521     content : {
19522         tag: 'tbody',
19523         cn: [
19524         {
19525             tag: 'tr',
19526             cn: [
19527             {
19528                 tag: 'td',
19529                 colspan: '7'
19530             }
19531             ]
19532         }
19533         ]
19534     },
19535     
19536     footer : {
19537         tag: 'tfoot',
19538         cn: [
19539         {
19540             tag: 'tr',
19541             cn: [
19542             {
19543                 tag: 'th',
19544                 colspan: '7',
19545                 cls: 'today'
19546             }
19547                     
19548             ]
19549         }
19550         ]
19551     },
19552     
19553     dates:{
19554         en: {
19555             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19556             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19557             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19558             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19559             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19560             today: "Today"
19561         }
19562     },
19563     
19564     modes: [
19565     {
19566         clsName: 'days',
19567         navFnc: 'Month',
19568         navStep: 1
19569     },
19570     {
19571         clsName: 'months',
19572         navFnc: 'FullYear',
19573         navStep: 1
19574     },
19575     {
19576         clsName: 'years',
19577         navFnc: 'FullYear',
19578         navStep: 10
19579     }]
19580 });
19581
19582 Roo.apply(Roo.bootstrap.DateField,  {
19583   
19584     template : {
19585         tag: 'div',
19586         cls: 'datepicker dropdown-menu roo-dynamic',
19587         cn: [
19588         {
19589             tag: 'div',
19590             cls: 'datepicker-days',
19591             cn: [
19592             {
19593                 tag: 'table',
19594                 cls: 'table-condensed',
19595                 cn:[
19596                 Roo.bootstrap.DateField.head,
19597                 {
19598                     tag: 'tbody'
19599                 },
19600                 Roo.bootstrap.DateField.footer
19601                 ]
19602             }
19603             ]
19604         },
19605         {
19606             tag: 'div',
19607             cls: 'datepicker-months',
19608             cn: [
19609             {
19610                 tag: 'table',
19611                 cls: 'table-condensed',
19612                 cn:[
19613                 Roo.bootstrap.DateField.head,
19614                 Roo.bootstrap.DateField.content,
19615                 Roo.bootstrap.DateField.footer
19616                 ]
19617             }
19618             ]
19619         },
19620         {
19621             tag: 'div',
19622             cls: 'datepicker-years',
19623             cn: [
19624             {
19625                 tag: 'table',
19626                 cls: 'table-condensed',
19627                 cn:[
19628                 Roo.bootstrap.DateField.head,
19629                 Roo.bootstrap.DateField.content,
19630                 Roo.bootstrap.DateField.footer
19631                 ]
19632             }
19633             ]
19634         }
19635         ]
19636     }
19637 });
19638
19639  
19640
19641  /*
19642  * - LGPL
19643  *
19644  * TimeField
19645  * 
19646  */
19647
19648 /**
19649  * @class Roo.bootstrap.TimeField
19650  * @extends Roo.bootstrap.Input
19651  * Bootstrap DateField class
19652  * 
19653  * 
19654  * @constructor
19655  * Create a new TimeField
19656  * @param {Object} config The config object
19657  */
19658
19659 Roo.bootstrap.TimeField = function(config){
19660     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19661     this.addEvents({
19662             /**
19663              * @event show
19664              * Fires when this field show.
19665              * @param {Roo.bootstrap.DateField} thisthis
19666              * @param {Mixed} date The date value
19667              */
19668             show : true,
19669             /**
19670              * @event show
19671              * Fires when this field hide.
19672              * @param {Roo.bootstrap.DateField} this
19673              * @param {Mixed} date The date value
19674              */
19675             hide : true,
19676             /**
19677              * @event select
19678              * Fires when select a date.
19679              * @param {Roo.bootstrap.DateField} this
19680              * @param {Mixed} date The date value
19681              */
19682             select : true
19683         });
19684 };
19685
19686 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19687     
19688     /**
19689      * @cfg {String} format
19690      * The default time format string which can be overriden for localization support.  The format must be
19691      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19692      */
19693     format : "H:i",
19694        
19695     onRender: function(ct, position)
19696     {
19697         
19698         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19699                 
19700         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19701         
19702         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19703         
19704         this.pop = this.picker().select('>.datepicker-time',true).first();
19705         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19706         
19707         this.picker().on('mousedown', this.onMousedown, this);
19708         this.picker().on('click', this.onClick, this);
19709         
19710         this.picker().addClass('datepicker-dropdown');
19711     
19712         this.fillTime();
19713         this.update();
19714             
19715         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19716         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19717         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19718         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19719         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19720         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19721
19722     },
19723     
19724     fireKey: function(e){
19725         if (!this.picker().isVisible()){
19726             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19727                 this.show();
19728             }
19729             return;
19730         }
19731
19732         e.preventDefault();
19733         
19734         switch(e.keyCode){
19735             case 27: // escape
19736                 this.hide();
19737                 break;
19738             case 37: // left
19739             case 39: // right
19740                 this.onTogglePeriod();
19741                 break;
19742             case 38: // up
19743                 this.onIncrementMinutes();
19744                 break;
19745             case 40: // down
19746                 this.onDecrementMinutes();
19747                 break;
19748             case 13: // enter
19749             case 9: // tab
19750                 this.setTime();
19751                 break;
19752         }
19753     },
19754     
19755     onClick: function(e) {
19756         e.stopPropagation();
19757         e.preventDefault();
19758     },
19759     
19760     picker : function()
19761     {
19762         return this.el.select('.datepicker', true).first();
19763     },
19764     
19765     fillTime: function()
19766     {    
19767         var time = this.pop.select('tbody', true).first();
19768         
19769         time.dom.innerHTML = '';
19770         
19771         time.createChild({
19772             tag: 'tr',
19773             cn: [
19774                 {
19775                     tag: 'td',
19776                     cn: [
19777                         {
19778                             tag: 'a',
19779                             href: '#',
19780                             cls: 'btn',
19781                             cn: [
19782                                 {
19783                                     tag: 'span',
19784                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19785                                 }
19786                             ]
19787                         } 
19788                     ]
19789                 },
19790                 {
19791                     tag: 'td',
19792                     cls: 'separator'
19793                 },
19794                 {
19795                     tag: 'td',
19796                     cn: [
19797                         {
19798                             tag: 'a',
19799                             href: '#',
19800                             cls: 'btn',
19801                             cn: [
19802                                 {
19803                                     tag: 'span',
19804                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19805                                 }
19806                             ]
19807                         }
19808                     ]
19809                 },
19810                 {
19811                     tag: 'td',
19812                     cls: 'separator'
19813                 }
19814             ]
19815         });
19816         
19817         time.createChild({
19818             tag: 'tr',
19819             cn: [
19820                 {
19821                     tag: 'td',
19822                     cn: [
19823                         {
19824                             tag: 'span',
19825                             cls: 'timepicker-hour',
19826                             html: '00'
19827                         }  
19828                     ]
19829                 },
19830                 {
19831                     tag: 'td',
19832                     cls: 'separator',
19833                     html: ':'
19834                 },
19835                 {
19836                     tag: 'td',
19837                     cn: [
19838                         {
19839                             tag: 'span',
19840                             cls: 'timepicker-minute',
19841                             html: '00'
19842                         }  
19843                     ]
19844                 },
19845                 {
19846                     tag: 'td',
19847                     cls: 'separator'
19848                 },
19849                 {
19850                     tag: 'td',
19851                     cn: [
19852                         {
19853                             tag: 'button',
19854                             type: 'button',
19855                             cls: 'btn btn-primary period',
19856                             html: 'AM'
19857                             
19858                         }
19859                     ]
19860                 }
19861             ]
19862         });
19863         
19864         time.createChild({
19865             tag: 'tr',
19866             cn: [
19867                 {
19868                     tag: 'td',
19869                     cn: [
19870                         {
19871                             tag: 'a',
19872                             href: '#',
19873                             cls: 'btn',
19874                             cn: [
19875                                 {
19876                                     tag: 'span',
19877                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19878                                 }
19879                             ]
19880                         }
19881                     ]
19882                 },
19883                 {
19884                     tag: 'td',
19885                     cls: 'separator'
19886                 },
19887                 {
19888                     tag: 'td',
19889                     cn: [
19890                         {
19891                             tag: 'a',
19892                             href: '#',
19893                             cls: 'btn',
19894                             cn: [
19895                                 {
19896                                     tag: 'span',
19897                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19898                                 }
19899                             ]
19900                         }
19901                     ]
19902                 },
19903                 {
19904                     tag: 'td',
19905                     cls: 'separator'
19906                 }
19907             ]
19908         });
19909         
19910     },
19911     
19912     update: function()
19913     {
19914         
19915         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19916         
19917         this.fill();
19918     },
19919     
19920     fill: function() 
19921     {
19922         var hours = this.time.getHours();
19923         var minutes = this.time.getMinutes();
19924         var period = 'AM';
19925         
19926         if(hours > 11){
19927             period = 'PM';
19928         }
19929         
19930         if(hours == 0){
19931             hours = 12;
19932         }
19933         
19934         
19935         if(hours > 12){
19936             hours = hours - 12;
19937         }
19938         
19939         if(hours < 10){
19940             hours = '0' + hours;
19941         }
19942         
19943         if(minutes < 10){
19944             minutes = '0' + minutes;
19945         }
19946         
19947         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19948         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19949         this.pop.select('button', true).first().dom.innerHTML = period;
19950         
19951     },
19952     
19953     place: function()
19954     {   
19955         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19956         
19957         var cls = ['bottom'];
19958         
19959         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19960             cls.pop();
19961             cls.push('top');
19962         }
19963         
19964         cls.push('right');
19965         
19966         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19967             cls.pop();
19968             cls.push('left');
19969         }
19970         
19971         this.picker().addClass(cls.join('-'));
19972         
19973         var _this = this;
19974         
19975         Roo.each(cls, function(c){
19976             if(c == 'bottom'){
19977                 _this.picker().setTop(_this.inputEl().getHeight());
19978                 return;
19979             }
19980             if(c == 'top'){
19981                 _this.picker().setTop(0 - _this.picker().getHeight());
19982                 return;
19983             }
19984             
19985             if(c == 'left'){
19986                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19987                 return;
19988             }
19989             if(c == 'right'){
19990                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19991                 return;
19992             }
19993         });
19994         
19995     },
19996   
19997     onFocus : function()
19998     {
19999         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20000         this.show();
20001     },
20002     
20003     onBlur : function()
20004     {
20005         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20006         this.hide();
20007     },
20008     
20009     show : function()
20010     {
20011         this.picker().show();
20012         this.pop.show();
20013         this.update();
20014         this.place();
20015         
20016         this.fireEvent('show', this, this.date);
20017     },
20018     
20019     hide : function()
20020     {
20021         this.picker().hide();
20022         this.pop.hide();
20023         
20024         this.fireEvent('hide', this, this.date);
20025     },
20026     
20027     setTime : function()
20028     {
20029         this.hide();
20030         this.setValue(this.time.format(this.format));
20031         
20032         this.fireEvent('select', this, this.date);
20033         
20034         
20035     },
20036     
20037     onMousedown: function(e){
20038         e.stopPropagation();
20039         e.preventDefault();
20040     },
20041     
20042     onIncrementHours: function()
20043     {
20044         Roo.log('onIncrementHours');
20045         this.time = this.time.add(Date.HOUR, 1);
20046         this.update();
20047         
20048     },
20049     
20050     onDecrementHours: function()
20051     {
20052         Roo.log('onDecrementHours');
20053         this.time = this.time.add(Date.HOUR, -1);
20054         this.update();
20055     },
20056     
20057     onIncrementMinutes: function()
20058     {
20059         Roo.log('onIncrementMinutes');
20060         this.time = this.time.add(Date.MINUTE, 1);
20061         this.update();
20062     },
20063     
20064     onDecrementMinutes: function()
20065     {
20066         Roo.log('onDecrementMinutes');
20067         this.time = this.time.add(Date.MINUTE, -1);
20068         this.update();
20069     },
20070     
20071     onTogglePeriod: function()
20072     {
20073         Roo.log('onTogglePeriod');
20074         this.time = this.time.add(Date.HOUR, 12);
20075         this.update();
20076     }
20077     
20078    
20079 });
20080
20081 Roo.apply(Roo.bootstrap.TimeField,  {
20082     
20083     content : {
20084         tag: 'tbody',
20085         cn: [
20086             {
20087                 tag: 'tr',
20088                 cn: [
20089                 {
20090                     tag: 'td',
20091                     colspan: '7'
20092                 }
20093                 ]
20094             }
20095         ]
20096     },
20097     
20098     footer : {
20099         tag: 'tfoot',
20100         cn: [
20101             {
20102                 tag: 'tr',
20103                 cn: [
20104                 {
20105                     tag: 'th',
20106                     colspan: '7',
20107                     cls: '',
20108                     cn: [
20109                         {
20110                             tag: 'button',
20111                             cls: 'btn btn-info ok',
20112                             html: 'OK'
20113                         }
20114                     ]
20115                 }
20116
20117                 ]
20118             }
20119         ]
20120     }
20121 });
20122
20123 Roo.apply(Roo.bootstrap.TimeField,  {
20124   
20125     template : {
20126         tag: 'div',
20127         cls: 'datepicker dropdown-menu',
20128         cn: [
20129             {
20130                 tag: 'div',
20131                 cls: 'datepicker-time',
20132                 cn: [
20133                 {
20134                     tag: 'table',
20135                     cls: 'table-condensed',
20136                     cn:[
20137                     Roo.bootstrap.TimeField.content,
20138                     Roo.bootstrap.TimeField.footer
20139                     ]
20140                 }
20141                 ]
20142             }
20143         ]
20144     }
20145 });
20146
20147  
20148
20149  /*
20150  * - LGPL
20151  *
20152  * MonthField
20153  * 
20154  */
20155
20156 /**
20157  * @class Roo.bootstrap.MonthField
20158  * @extends Roo.bootstrap.Input
20159  * Bootstrap MonthField class
20160  * 
20161  * @cfg {String} language default en
20162  * 
20163  * @constructor
20164  * Create a new MonthField
20165  * @param {Object} config The config object
20166  */
20167
20168 Roo.bootstrap.MonthField = function(config){
20169     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20170     
20171     this.addEvents({
20172         /**
20173          * @event show
20174          * Fires when this field show.
20175          * @param {Roo.bootstrap.MonthField} this
20176          * @param {Mixed} date The date value
20177          */
20178         show : true,
20179         /**
20180          * @event show
20181          * Fires when this field hide.
20182          * @param {Roo.bootstrap.MonthField} this
20183          * @param {Mixed} date The date value
20184          */
20185         hide : true,
20186         /**
20187          * @event select
20188          * Fires when select a date.
20189          * @param {Roo.bootstrap.MonthField} this
20190          * @param {String} oldvalue The old value
20191          * @param {String} newvalue The new value
20192          */
20193         select : true
20194     });
20195 };
20196
20197 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20198     
20199     onRender: function(ct, position)
20200     {
20201         
20202         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20203         
20204         this.language = this.language || 'en';
20205         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20206         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20207         
20208         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20209         this.isInline = false;
20210         this.isInput = true;
20211         this.component = this.el.select('.add-on', true).first() || false;
20212         this.component = (this.component && this.component.length === 0) ? false : this.component;
20213         this.hasInput = this.component && this.inputEL().length;
20214         
20215         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20216         
20217         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20218         
20219         this.picker().on('mousedown', this.onMousedown, this);
20220         this.picker().on('click', this.onClick, this);
20221         
20222         this.picker().addClass('datepicker-dropdown');
20223         
20224         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20225             v.setStyle('width', '189px');
20226         });
20227         
20228         this.fillMonths();
20229         
20230         this.update();
20231         
20232         if(this.isInline) {
20233             this.show();
20234         }
20235         
20236     },
20237     
20238     setValue: function(v, suppressEvent)
20239     {   
20240         var o = this.getValue();
20241         
20242         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20243         
20244         this.update();
20245
20246         if(suppressEvent !== true){
20247             this.fireEvent('select', this, o, v);
20248         }
20249         
20250     },
20251     
20252     getValue: function()
20253     {
20254         return this.value;
20255     },
20256     
20257     onClick: function(e) 
20258     {
20259         e.stopPropagation();
20260         e.preventDefault();
20261         
20262         var target = e.getTarget();
20263         
20264         if(target.nodeName.toLowerCase() === 'i'){
20265             target = Roo.get(target).dom.parentNode;
20266         }
20267         
20268         var nodeName = target.nodeName;
20269         var className = target.className;
20270         var html = target.innerHTML;
20271         
20272         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20273             return;
20274         }
20275         
20276         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20277         
20278         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20279         
20280         this.hide();
20281                         
20282     },
20283     
20284     picker : function()
20285     {
20286         return this.pickerEl;
20287     },
20288     
20289     fillMonths: function()
20290     {    
20291         var i = 0;
20292         var months = this.picker().select('>.datepicker-months td', true).first();
20293         
20294         months.dom.innerHTML = '';
20295         
20296         while (i < 12) {
20297             var month = {
20298                 tag: 'span',
20299                 cls: 'month',
20300                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20301             };
20302             
20303             months.createChild(month);
20304         }
20305         
20306     },
20307     
20308     update: function()
20309     {
20310         var _this = this;
20311         
20312         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20313             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20314         }
20315         
20316         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20317             e.removeClass('active');
20318             
20319             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20320                 e.addClass('active');
20321             }
20322         })
20323     },
20324     
20325     place: function()
20326     {
20327         if(this.isInline) {
20328             return;
20329         }
20330         
20331         this.picker().removeClass(['bottom', 'top']);
20332         
20333         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20334             /*
20335              * place to the top of element!
20336              *
20337              */
20338             
20339             this.picker().addClass('top');
20340             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20341             
20342             return;
20343         }
20344         
20345         this.picker().addClass('bottom');
20346         
20347         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20348     },
20349     
20350     onFocus : function()
20351     {
20352         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20353         this.show();
20354     },
20355     
20356     onBlur : function()
20357     {
20358         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20359         
20360         var d = this.inputEl().getValue();
20361         
20362         this.setValue(d);
20363                 
20364         this.hide();
20365     },
20366     
20367     show : function()
20368     {
20369         this.picker().show();
20370         this.picker().select('>.datepicker-months', true).first().show();
20371         this.update();
20372         this.place();
20373         
20374         this.fireEvent('show', this, this.date);
20375     },
20376     
20377     hide : function()
20378     {
20379         if(this.isInline) {
20380             return;
20381         }
20382         this.picker().hide();
20383         this.fireEvent('hide', this, this.date);
20384         
20385     },
20386     
20387     onMousedown: function(e)
20388     {
20389         e.stopPropagation();
20390         e.preventDefault();
20391     },
20392     
20393     keyup: function(e)
20394     {
20395         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20396         this.update();
20397     },
20398
20399     fireKey: function(e)
20400     {
20401         if (!this.picker().isVisible()){
20402             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20403                 this.show();
20404             }
20405             return;
20406         }
20407         
20408         var dir;
20409         
20410         switch(e.keyCode){
20411             case 27: // escape
20412                 this.hide();
20413                 e.preventDefault();
20414                 break;
20415             case 37: // left
20416             case 39: // right
20417                 dir = e.keyCode == 37 ? -1 : 1;
20418                 
20419                 this.vIndex = this.vIndex + dir;
20420                 
20421                 if(this.vIndex < 0){
20422                     this.vIndex = 0;
20423                 }
20424                 
20425                 if(this.vIndex > 11){
20426                     this.vIndex = 11;
20427                 }
20428                 
20429                 if(isNaN(this.vIndex)){
20430                     this.vIndex = 0;
20431                 }
20432                 
20433                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20434                 
20435                 break;
20436             case 38: // up
20437             case 40: // down
20438                 
20439                 dir = e.keyCode == 38 ? -1 : 1;
20440                 
20441                 this.vIndex = this.vIndex + dir * 4;
20442                 
20443                 if(this.vIndex < 0){
20444                     this.vIndex = 0;
20445                 }
20446                 
20447                 if(this.vIndex > 11){
20448                     this.vIndex = 11;
20449                 }
20450                 
20451                 if(isNaN(this.vIndex)){
20452                     this.vIndex = 0;
20453                 }
20454                 
20455                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20456                 break;
20457                 
20458             case 13: // enter
20459                 
20460                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20461                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20462                 }
20463                 
20464                 this.hide();
20465                 e.preventDefault();
20466                 break;
20467             case 9: // tab
20468                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20469                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20470                 }
20471                 this.hide();
20472                 break;
20473             case 16: // shift
20474             case 17: // ctrl
20475             case 18: // alt
20476                 break;
20477             default :
20478                 this.hide();
20479                 
20480         }
20481     },
20482     
20483     remove: function() 
20484     {
20485         this.picker().remove();
20486     }
20487    
20488 });
20489
20490 Roo.apply(Roo.bootstrap.MonthField,  {
20491     
20492     content : {
20493         tag: 'tbody',
20494         cn: [
20495         {
20496             tag: 'tr',
20497             cn: [
20498             {
20499                 tag: 'td',
20500                 colspan: '7'
20501             }
20502             ]
20503         }
20504         ]
20505     },
20506     
20507     dates:{
20508         en: {
20509             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20510             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20511         }
20512     }
20513 });
20514
20515 Roo.apply(Roo.bootstrap.MonthField,  {
20516   
20517     template : {
20518         tag: 'div',
20519         cls: 'datepicker dropdown-menu roo-dynamic',
20520         cn: [
20521             {
20522                 tag: 'div',
20523                 cls: 'datepicker-months',
20524                 cn: [
20525                 {
20526                     tag: 'table',
20527                     cls: 'table-condensed',
20528                     cn:[
20529                         Roo.bootstrap.DateField.content
20530                     ]
20531                 }
20532                 ]
20533             }
20534         ]
20535     }
20536 });
20537
20538  
20539
20540  
20541  /*
20542  * - LGPL
20543  *
20544  * CheckBox
20545  * 
20546  */
20547
20548 /**
20549  * @class Roo.bootstrap.CheckBox
20550  * @extends Roo.bootstrap.Input
20551  * Bootstrap CheckBox class
20552  * 
20553  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20554  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20555  * @cfg {String} boxLabel The text that appears beside the checkbox
20556  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20557  * @cfg {Boolean} checked initnal the element
20558  * @cfg {Boolean} inline inline the element (default false)
20559  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20560  * @cfg {String} tooltip label tooltip
20561  * 
20562  * @constructor
20563  * Create a new CheckBox
20564  * @param {Object} config The config object
20565  */
20566
20567 Roo.bootstrap.CheckBox = function(config){
20568     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20569    
20570     this.addEvents({
20571         /**
20572         * @event check
20573         * Fires when the element is checked or unchecked.
20574         * @param {Roo.bootstrap.CheckBox} this This input
20575         * @param {Boolean} checked The new checked value
20576         */
20577        check : true,
20578        /**
20579         * @event click
20580         * Fires when the element is click.
20581         * @param {Roo.bootstrap.CheckBox} this This input
20582         */
20583        click : true
20584     });
20585     
20586 };
20587
20588 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20589   
20590     inputType: 'checkbox',
20591     inputValue: 1,
20592     valueOff: 0,
20593     boxLabel: false,
20594     checked: false,
20595     weight : false,
20596     inline: false,
20597     tooltip : '',
20598     
20599     getAutoCreate : function()
20600     {
20601         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20602         
20603         var id = Roo.id();
20604         
20605         var cfg = {};
20606         
20607         cfg.cls = 'form-group ' + this.inputType; //input-group
20608         
20609         if(this.inline){
20610             cfg.cls += ' ' + this.inputType + '-inline';
20611         }
20612         
20613         var input =  {
20614             tag: 'input',
20615             id : id,
20616             type : this.inputType,
20617             value : this.inputValue,
20618             cls : 'roo-' + this.inputType, //'form-box',
20619             placeholder : this.placeholder || ''
20620             
20621         };
20622         
20623         if(this.inputType != 'radio'){
20624             var hidden =  {
20625                 tag: 'input',
20626                 type : 'hidden',
20627                 cls : 'roo-hidden-value',
20628                 value : this.checked ? this.inputValue : this.valueOff
20629             };
20630         }
20631         
20632             
20633         if (this.weight) { // Validity check?
20634             cfg.cls += " " + this.inputType + "-" + this.weight;
20635         }
20636         
20637         if (this.disabled) {
20638             input.disabled=true;
20639         }
20640         
20641         if(this.checked){
20642             input.checked = this.checked;
20643         }
20644         
20645         if (this.name) {
20646             
20647             input.name = this.name;
20648             
20649             if(this.inputType != 'radio'){
20650                 hidden.name = this.name;
20651                 input.name = '_hidden_' + this.name;
20652             }
20653         }
20654         
20655         if (this.size) {
20656             input.cls += ' input-' + this.size;
20657         }
20658         
20659         var settings=this;
20660         
20661         ['xs','sm','md','lg'].map(function(size){
20662             if (settings[size]) {
20663                 cfg.cls += ' col-' + size + '-' + settings[size];
20664             }
20665         });
20666         
20667         var inputblock = input;
20668          
20669         if (this.before || this.after) {
20670             
20671             inputblock = {
20672                 cls : 'input-group',
20673                 cn :  [] 
20674             };
20675             
20676             if (this.before) {
20677                 inputblock.cn.push({
20678                     tag :'span',
20679                     cls : 'input-group-addon',
20680                     html : this.before
20681                 });
20682             }
20683             
20684             inputblock.cn.push(input);
20685             
20686             if(this.inputType != 'radio'){
20687                 inputblock.cn.push(hidden);
20688             }
20689             
20690             if (this.after) {
20691                 inputblock.cn.push({
20692                     tag :'span',
20693                     cls : 'input-group-addon',
20694                     html : this.after
20695                 });
20696             }
20697             
20698         }
20699         
20700         if (align ==='left' && this.fieldLabel.length) {
20701 //                Roo.log("left and has label");
20702             cfg.cn = [
20703                 {
20704                     tag: 'label',
20705                     'for' :  id,
20706                     cls : 'control-label',
20707                     html : this.fieldLabel
20708                 },
20709                 {
20710                     cls : "", 
20711                     cn: [
20712                         inputblock
20713                     ]
20714                 }
20715             ];
20716             
20717             if(this.labelWidth > 12){
20718                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20719             }
20720             
20721             if(this.labelWidth < 13 && this.labelmd == 0){
20722                 this.labelmd = this.labelWidth;
20723             }
20724             
20725             if(this.labellg > 0){
20726                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20727                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20728             }
20729             
20730             if(this.labelmd > 0){
20731                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20732                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20733             }
20734             
20735             if(this.labelsm > 0){
20736                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20737                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20738             }
20739             
20740             if(this.labelxs > 0){
20741                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20742                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20743             }
20744             
20745         } else if ( this.fieldLabel.length) {
20746 //                Roo.log(" label");
20747                 cfg.cn = [
20748                    
20749                     {
20750                         tag: this.boxLabel ? 'span' : 'label',
20751                         'for': id,
20752                         cls: 'control-label box-input-label',
20753                         //cls : 'input-group-addon',
20754                         html : this.fieldLabel
20755                     },
20756                     
20757                     inputblock
20758                     
20759                 ];
20760
20761         } else {
20762             
20763 //                Roo.log(" no label && no align");
20764                 cfg.cn = [  inputblock ] ;
20765                 
20766                 
20767         }
20768         
20769         if(this.boxLabel){
20770              var boxLabelCfg = {
20771                 tag: 'label',
20772                 //'for': id, // box label is handled by onclick - so no for...
20773                 cls: 'box-label',
20774                 html: this.boxLabel
20775             };
20776             
20777             if(this.tooltip){
20778                 boxLabelCfg.tooltip = this.tooltip;
20779             }
20780              
20781             cfg.cn.push(boxLabelCfg);
20782         }
20783         
20784         if(this.inputType != 'radio'){
20785             cfg.cn.push(hidden);
20786         }
20787         
20788         return cfg;
20789         
20790     },
20791     
20792     /**
20793      * return the real input element.
20794      */
20795     inputEl: function ()
20796     {
20797         return this.el.select('input.roo-' + this.inputType,true).first();
20798     },
20799     hiddenEl: function ()
20800     {
20801         return this.el.select('input.roo-hidden-value',true).first();
20802     },
20803     
20804     labelEl: function()
20805     {
20806         return this.el.select('label.control-label',true).first();
20807     },
20808     /* depricated... */
20809     
20810     label: function()
20811     {
20812         return this.labelEl();
20813     },
20814     
20815     boxLabelEl: function()
20816     {
20817         return this.el.select('label.box-label',true).first();
20818     },
20819     
20820     initEvents : function()
20821     {
20822 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20823         
20824         this.inputEl().on('click', this.onClick,  this);
20825         
20826         if (this.boxLabel) { 
20827             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20828         }
20829         
20830         this.startValue = this.getValue();
20831         
20832         if(this.groupId){
20833             Roo.bootstrap.CheckBox.register(this);
20834         }
20835     },
20836     
20837     onClick : function(e)
20838     {   
20839         if(this.fireEvent('click', this, e) !== false){
20840             this.setChecked(!this.checked);
20841         }
20842         
20843     },
20844     
20845     setChecked : function(state,suppressEvent)
20846     {
20847         this.startValue = this.getValue();
20848
20849         if(this.inputType == 'radio'){
20850             
20851             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20852                 e.dom.checked = false;
20853             });
20854             
20855             this.inputEl().dom.checked = true;
20856             
20857             this.inputEl().dom.value = this.inputValue;
20858             
20859             if(suppressEvent !== true){
20860                 this.fireEvent('check', this, true);
20861             }
20862             
20863             this.validate();
20864             
20865             return;
20866         }
20867         
20868         this.checked = state;
20869         
20870         this.inputEl().dom.checked = state;
20871         
20872         
20873         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20874         
20875         if(suppressEvent !== true){
20876             this.fireEvent('check', this, state);
20877         }
20878         
20879         this.validate();
20880     },
20881     
20882     getValue : function()
20883     {
20884         if(this.inputType == 'radio'){
20885             return this.getGroupValue();
20886         }
20887         
20888         return this.hiddenEl().dom.value;
20889         
20890     },
20891     
20892     getGroupValue : function()
20893     {
20894         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20895             return '';
20896         }
20897         
20898         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20899     },
20900     
20901     setValue : function(v,suppressEvent)
20902     {
20903         if(this.inputType == 'radio'){
20904             this.setGroupValue(v, suppressEvent);
20905             return;
20906         }
20907         
20908         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20909         
20910         this.validate();
20911     },
20912     
20913     setGroupValue : function(v, suppressEvent)
20914     {
20915         this.startValue = this.getValue();
20916         
20917         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20918             e.dom.checked = false;
20919             
20920             if(e.dom.value == v){
20921                 e.dom.checked = true;
20922             }
20923         });
20924         
20925         if(suppressEvent !== true){
20926             this.fireEvent('check', this, true);
20927         }
20928
20929         this.validate();
20930         
20931         return;
20932     },
20933     
20934     validate : function()
20935     {
20936         if(this.getVisibilityEl().hasClass('hidden')){
20937             return true;
20938         }
20939         
20940         if(
20941                 this.disabled || 
20942                 (this.inputType == 'radio' && this.validateRadio()) ||
20943                 (this.inputType == 'checkbox' && this.validateCheckbox())
20944         ){
20945             this.markValid();
20946             return true;
20947         }
20948         
20949         this.markInvalid();
20950         return false;
20951     },
20952     
20953     validateRadio : function()
20954     {
20955         if(this.getVisibilityEl().hasClass('hidden')){
20956             return true;
20957         }
20958         
20959         if(this.allowBlank){
20960             return true;
20961         }
20962         
20963         var valid = false;
20964         
20965         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20966             if(!e.dom.checked){
20967                 return;
20968             }
20969             
20970             valid = true;
20971             
20972             return false;
20973         });
20974         
20975         return valid;
20976     },
20977     
20978     validateCheckbox : function()
20979     {
20980         if(!this.groupId){
20981             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20982             //return (this.getValue() == this.inputValue) ? true : false;
20983         }
20984         
20985         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20986         
20987         if(!group){
20988             return false;
20989         }
20990         
20991         var r = false;
20992         
20993         for(var i in group){
20994             if(group[i].el.isVisible(true)){
20995                 r = false;
20996                 break;
20997             }
20998             
20999             r = true;
21000         }
21001         
21002         for(var i in group){
21003             if(r){
21004                 break;
21005             }
21006             
21007             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21008         }
21009         
21010         return r;
21011     },
21012     
21013     /**
21014      * Mark this field as valid
21015      */
21016     markValid : function()
21017     {
21018         var _this = this;
21019         
21020         this.fireEvent('valid', this);
21021         
21022         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21023         
21024         if(this.groupId){
21025             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21026         }
21027         
21028         if(label){
21029             label.markValid();
21030         }
21031
21032         if(this.inputType == 'radio'){
21033             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21034                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21035                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21036             });
21037             
21038             return;
21039         }
21040
21041         if(!this.groupId){
21042             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21043             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21044             return;
21045         }
21046         
21047         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21048         
21049         if(!group){
21050             return;
21051         }
21052         
21053         for(var i in group){
21054             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21055             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21056         }
21057     },
21058     
21059      /**
21060      * Mark this field as invalid
21061      * @param {String} msg The validation message
21062      */
21063     markInvalid : function(msg)
21064     {
21065         if(this.allowBlank){
21066             return;
21067         }
21068         
21069         var _this = this;
21070         
21071         this.fireEvent('invalid', this, msg);
21072         
21073         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21074         
21075         if(this.groupId){
21076             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21077         }
21078         
21079         if(label){
21080             label.markInvalid();
21081         }
21082             
21083         if(this.inputType == 'radio'){
21084             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21085                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21086                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21087             });
21088             
21089             return;
21090         }
21091         
21092         if(!this.groupId){
21093             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21094             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21095             return;
21096         }
21097         
21098         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21099         
21100         if(!group){
21101             return;
21102         }
21103         
21104         for(var i in group){
21105             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21106             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21107         }
21108         
21109     },
21110     
21111     clearInvalid : function()
21112     {
21113         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21114         
21115         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21116         
21117         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21118         
21119         if (label && label.iconEl) {
21120             label.iconEl.removeClass(label.validClass);
21121             label.iconEl.removeClass(label.invalidClass);
21122         }
21123     },
21124     
21125     disable : function()
21126     {
21127         if(this.inputType != 'radio'){
21128             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21129             return;
21130         }
21131         
21132         var _this = this;
21133         
21134         if(this.rendered){
21135             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21136                 _this.getActionEl().addClass(this.disabledClass);
21137                 e.dom.disabled = true;
21138             });
21139         }
21140         
21141         this.disabled = true;
21142         this.fireEvent("disable", this);
21143         return this;
21144     },
21145
21146     enable : function()
21147     {
21148         if(this.inputType != 'radio'){
21149             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21150             return;
21151         }
21152         
21153         var _this = this;
21154         
21155         if(this.rendered){
21156             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21157                 _this.getActionEl().removeClass(this.disabledClass);
21158                 e.dom.disabled = false;
21159             });
21160         }
21161         
21162         this.disabled = false;
21163         this.fireEvent("enable", this);
21164         return this;
21165     },
21166     
21167     setBoxLabel : function(v)
21168     {
21169         this.boxLabel = v;
21170         
21171         if(this.rendered){
21172             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21173         }
21174     }
21175
21176 });
21177
21178 Roo.apply(Roo.bootstrap.CheckBox, {
21179     
21180     groups: {},
21181     
21182      /**
21183     * register a CheckBox Group
21184     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21185     */
21186     register : function(checkbox)
21187     {
21188         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21189             this.groups[checkbox.groupId] = {};
21190         }
21191         
21192         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21193             return;
21194         }
21195         
21196         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21197         
21198     },
21199     /**
21200     * fetch a CheckBox Group based on the group ID
21201     * @param {string} the group ID
21202     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21203     */
21204     get: function(groupId) {
21205         if (typeof(this.groups[groupId]) == 'undefined') {
21206             return false;
21207         }
21208         
21209         return this.groups[groupId] ;
21210     }
21211     
21212     
21213 });
21214 /*
21215  * - LGPL
21216  *
21217  * RadioItem
21218  * 
21219  */
21220
21221 /**
21222  * @class Roo.bootstrap.Radio
21223  * @extends Roo.bootstrap.Component
21224  * Bootstrap Radio class
21225  * @cfg {String} boxLabel - the label associated
21226  * @cfg {String} value - the value of radio
21227  * 
21228  * @constructor
21229  * Create a new Radio
21230  * @param {Object} config The config object
21231  */
21232 Roo.bootstrap.Radio = function(config){
21233     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21234     
21235 };
21236
21237 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21238     
21239     boxLabel : '',
21240     
21241     value : '',
21242     
21243     getAutoCreate : function()
21244     {
21245         var cfg = {
21246             tag : 'div',
21247             cls : 'form-group radio',
21248             cn : [
21249                 {
21250                     tag : 'label',
21251                     cls : 'box-label',
21252                     html : this.boxLabel
21253                 }
21254             ]
21255         };
21256         
21257         return cfg;
21258     },
21259     
21260     initEvents : function() 
21261     {
21262         this.parent().register(this);
21263         
21264         this.el.on('click', this.onClick, this);
21265         
21266     },
21267     
21268     onClick : function(e)
21269     {
21270         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21271             this.setChecked(true);
21272         }
21273     },
21274     
21275     setChecked : function(state, suppressEvent)
21276     {
21277         this.parent().setValue(this.value, suppressEvent);
21278         
21279     },
21280     
21281     setBoxLabel : function(v)
21282     {
21283         this.boxLabel = v;
21284         
21285         if(this.rendered){
21286             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21287         }
21288     }
21289     
21290 });
21291  
21292
21293  /*
21294  * - LGPL
21295  *
21296  * Input
21297  * 
21298  */
21299
21300 /**
21301  * @class Roo.bootstrap.SecurePass
21302  * @extends Roo.bootstrap.Input
21303  * Bootstrap SecurePass class
21304  *
21305  * 
21306  * @constructor
21307  * Create a new SecurePass
21308  * @param {Object} config The config object
21309  */
21310  
21311 Roo.bootstrap.SecurePass = function (config) {
21312     // these go here, so the translation tool can replace them..
21313     this.errors = {
21314         PwdEmpty: "Please type a password, and then retype it to confirm.",
21315         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21316         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21317         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21318         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21319         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21320         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21321         TooWeak: "Your password is Too Weak."
21322     },
21323     this.meterLabel = "Password strength:";
21324     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21325     this.meterClass = [
21326         "roo-password-meter-tooweak", 
21327         "roo-password-meter-weak", 
21328         "roo-password-meter-medium", 
21329         "roo-password-meter-strong", 
21330         "roo-password-meter-grey"
21331     ];
21332     
21333     this.errors = {};
21334     
21335     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21336 }
21337
21338 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21339     /**
21340      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21341      * {
21342      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21343      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21344      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21345      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21346      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21347      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21348      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21349      * })
21350      */
21351     // private
21352     
21353     meterWidth: 300,
21354     errorMsg :'',    
21355     errors: false,
21356     imageRoot: '/',
21357     /**
21358      * @cfg {String/Object} Label for the strength meter (defaults to
21359      * 'Password strength:')
21360      */
21361     // private
21362     meterLabel: '',
21363     /**
21364      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21365      * ['Weak', 'Medium', 'Strong'])
21366      */
21367     // private    
21368     pwdStrengths: false,    
21369     // private
21370     strength: 0,
21371     // private
21372     _lastPwd: null,
21373     // private
21374     kCapitalLetter: 0,
21375     kSmallLetter: 1,
21376     kDigit: 2,
21377     kPunctuation: 3,
21378     
21379     insecure: false,
21380     // private
21381     initEvents: function ()
21382     {
21383         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21384
21385         if (this.el.is('input[type=password]') && Roo.isSafari) {
21386             this.el.on('keydown', this.SafariOnKeyDown, this);
21387         }
21388
21389         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21390     },
21391     // private
21392     onRender: function (ct, position)
21393     {
21394         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21395         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21396         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21397
21398         this.trigger.createChild({
21399                    cn: [
21400                     {
21401                     //id: 'PwdMeter',
21402                     tag: 'div',
21403                     cls: 'roo-password-meter-grey col-xs-12',
21404                     style: {
21405                         //width: 0,
21406                         //width: this.meterWidth + 'px'                                                
21407                         }
21408                     },
21409                     {                            
21410                          cls: 'roo-password-meter-text'                          
21411                     }
21412                 ]            
21413         });
21414
21415          
21416         if (this.hideTrigger) {
21417             this.trigger.setDisplayed(false);
21418         }
21419         this.setSize(this.width || '', this.height || '');
21420     },
21421     // private
21422     onDestroy: function ()
21423     {
21424         if (this.trigger) {
21425             this.trigger.removeAllListeners();
21426             this.trigger.remove();
21427         }
21428         if (this.wrap) {
21429             this.wrap.remove();
21430         }
21431         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21432     },
21433     // private
21434     checkStrength: function ()
21435     {
21436         var pwd = this.inputEl().getValue();
21437         if (pwd == this._lastPwd) {
21438             return;
21439         }
21440
21441         var strength;
21442         if (this.ClientSideStrongPassword(pwd)) {
21443             strength = 3;
21444         } else if (this.ClientSideMediumPassword(pwd)) {
21445             strength = 2;
21446         } else if (this.ClientSideWeakPassword(pwd)) {
21447             strength = 1;
21448         } else {
21449             strength = 0;
21450         }
21451         
21452         Roo.log('strength1: ' + strength);
21453         
21454         //var pm = this.trigger.child('div/div/div').dom;
21455         var pm = this.trigger.child('div/div');
21456         pm.removeClass(this.meterClass);
21457         pm.addClass(this.meterClass[strength]);
21458                 
21459         
21460         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21461                 
21462         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21463         
21464         this._lastPwd = pwd;
21465     },
21466     reset: function ()
21467     {
21468         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21469         
21470         this._lastPwd = '';
21471         
21472         var pm = this.trigger.child('div/div');
21473         pm.removeClass(this.meterClass);
21474         pm.addClass('roo-password-meter-grey');        
21475         
21476         
21477         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21478         
21479         pt.innerHTML = '';
21480         this.inputEl().dom.type='password';
21481     },
21482     // private
21483     validateValue: function (value)
21484     {
21485         
21486         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21487             return false;
21488         }
21489         if (value.length == 0) {
21490             if (this.allowBlank) {
21491                 this.clearInvalid();
21492                 return true;
21493             }
21494
21495             this.markInvalid(this.errors.PwdEmpty);
21496             this.errorMsg = this.errors.PwdEmpty;
21497             return false;
21498         }
21499         
21500         if(this.insecure){
21501             return true;
21502         }
21503         
21504         if ('[\x21-\x7e]*'.match(value)) {
21505             this.markInvalid(this.errors.PwdBadChar);
21506             this.errorMsg = this.errors.PwdBadChar;
21507             return false;
21508         }
21509         if (value.length < 6) {
21510             this.markInvalid(this.errors.PwdShort);
21511             this.errorMsg = this.errors.PwdShort;
21512             return false;
21513         }
21514         if (value.length > 16) {
21515             this.markInvalid(this.errors.PwdLong);
21516             this.errorMsg = this.errors.PwdLong;
21517             return false;
21518         }
21519         var strength;
21520         if (this.ClientSideStrongPassword(value)) {
21521             strength = 3;
21522         } else if (this.ClientSideMediumPassword(value)) {
21523             strength = 2;
21524         } else if (this.ClientSideWeakPassword(value)) {
21525             strength = 1;
21526         } else {
21527             strength = 0;
21528         }
21529
21530         
21531         if (strength < 2) {
21532             //this.markInvalid(this.errors.TooWeak);
21533             this.errorMsg = this.errors.TooWeak;
21534             //return false;
21535         }
21536         
21537         
21538         console.log('strength2: ' + strength);
21539         
21540         //var pm = this.trigger.child('div/div/div').dom;
21541         
21542         var pm = this.trigger.child('div/div');
21543         pm.removeClass(this.meterClass);
21544         pm.addClass(this.meterClass[strength]);
21545                 
21546         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21547                 
21548         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21549         
21550         this.errorMsg = ''; 
21551         return true;
21552     },
21553     // private
21554     CharacterSetChecks: function (type)
21555     {
21556         this.type = type;
21557         this.fResult = false;
21558     },
21559     // private
21560     isctype: function (character, type)
21561     {
21562         switch (type) {  
21563             case this.kCapitalLetter:
21564                 if (character >= 'A' && character <= 'Z') {
21565                     return true;
21566                 }
21567                 break;
21568             
21569             case this.kSmallLetter:
21570                 if (character >= 'a' && character <= 'z') {
21571                     return true;
21572                 }
21573                 break;
21574             
21575             case this.kDigit:
21576                 if (character >= '0' && character <= '9') {
21577                     return true;
21578                 }
21579                 break;
21580             
21581             case this.kPunctuation:
21582                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21583                     return true;
21584                 }
21585                 break;
21586             
21587             default:
21588                 return false;
21589         }
21590
21591     },
21592     // private
21593     IsLongEnough: function (pwd, size)
21594     {
21595         return !(pwd == null || isNaN(size) || pwd.length < size);
21596     },
21597     // private
21598     SpansEnoughCharacterSets: function (word, nb)
21599     {
21600         if (!this.IsLongEnough(word, nb))
21601         {
21602             return false;
21603         }
21604
21605         var characterSetChecks = new Array(
21606             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21607             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21608         );
21609         
21610         for (var index = 0; index < word.length; ++index) {
21611             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21612                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21613                     characterSetChecks[nCharSet].fResult = true;
21614                     break;
21615                 }
21616             }
21617         }
21618
21619         var nCharSets = 0;
21620         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21621             if (characterSetChecks[nCharSet].fResult) {
21622                 ++nCharSets;
21623             }
21624         }
21625
21626         if (nCharSets < nb) {
21627             return false;
21628         }
21629         return true;
21630     },
21631     // private
21632     ClientSideStrongPassword: function (pwd)
21633     {
21634         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21635     },
21636     // private
21637     ClientSideMediumPassword: function (pwd)
21638     {
21639         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21640     },
21641     // private
21642     ClientSideWeakPassword: function (pwd)
21643     {
21644         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21645     }
21646           
21647 })//<script type="text/javascript">
21648
21649 /*
21650  * Based  Ext JS Library 1.1.1
21651  * Copyright(c) 2006-2007, Ext JS, LLC.
21652  * LGPL
21653  *
21654  */
21655  
21656 /**
21657  * @class Roo.HtmlEditorCore
21658  * @extends Roo.Component
21659  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21660  *
21661  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21662  */
21663
21664 Roo.HtmlEditorCore = function(config){
21665     
21666     
21667     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21668     
21669     
21670     this.addEvents({
21671         /**
21672          * @event initialize
21673          * Fires when the editor is fully initialized (including the iframe)
21674          * @param {Roo.HtmlEditorCore} this
21675          */
21676         initialize: true,
21677         /**
21678          * @event activate
21679          * Fires when the editor is first receives the focus. Any insertion must wait
21680          * until after this event.
21681          * @param {Roo.HtmlEditorCore} this
21682          */
21683         activate: true,
21684          /**
21685          * @event beforesync
21686          * Fires before the textarea is updated with content from the editor iframe. Return false
21687          * to cancel the sync.
21688          * @param {Roo.HtmlEditorCore} this
21689          * @param {String} html
21690          */
21691         beforesync: true,
21692          /**
21693          * @event beforepush
21694          * Fires before the iframe editor is updated with content from the textarea. Return false
21695          * to cancel the push.
21696          * @param {Roo.HtmlEditorCore} this
21697          * @param {String} html
21698          */
21699         beforepush: true,
21700          /**
21701          * @event sync
21702          * Fires when the textarea is updated with content from the editor iframe.
21703          * @param {Roo.HtmlEditorCore} this
21704          * @param {String} html
21705          */
21706         sync: true,
21707          /**
21708          * @event push
21709          * Fires when the iframe editor is updated with content from the textarea.
21710          * @param {Roo.HtmlEditorCore} this
21711          * @param {String} html
21712          */
21713         push: true,
21714         
21715         /**
21716          * @event editorevent
21717          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21718          * @param {Roo.HtmlEditorCore} this
21719          */
21720         editorevent: true
21721         
21722     });
21723     
21724     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21725     
21726     // defaults : white / black...
21727     this.applyBlacklists();
21728     
21729     
21730     
21731 };
21732
21733
21734 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21735
21736
21737      /**
21738      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21739      */
21740     
21741     owner : false,
21742     
21743      /**
21744      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21745      *                        Roo.resizable.
21746      */
21747     resizable : false,
21748      /**
21749      * @cfg {Number} height (in pixels)
21750      */   
21751     height: 300,
21752    /**
21753      * @cfg {Number} width (in pixels)
21754      */   
21755     width: 500,
21756     
21757     /**
21758      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21759      * 
21760      */
21761     stylesheets: false,
21762     
21763     // id of frame..
21764     frameId: false,
21765     
21766     // private properties
21767     validationEvent : false,
21768     deferHeight: true,
21769     initialized : false,
21770     activated : false,
21771     sourceEditMode : false,
21772     onFocus : Roo.emptyFn,
21773     iframePad:3,
21774     hideMode:'offsets',
21775     
21776     clearUp: true,
21777     
21778     // blacklist + whitelisted elements..
21779     black: false,
21780     white: false,
21781      
21782     bodyCls : '',
21783
21784     /**
21785      * Protected method that will not generally be called directly. It
21786      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21787      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21788      */
21789     getDocMarkup : function(){
21790         // body styles..
21791         var st = '';
21792         
21793         // inherit styels from page...?? 
21794         if (this.stylesheets === false) {
21795             
21796             Roo.get(document.head).select('style').each(function(node) {
21797                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21798             });
21799             
21800             Roo.get(document.head).select('link').each(function(node) { 
21801                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21802             });
21803             
21804         } else if (!this.stylesheets.length) {
21805                 // simple..
21806                 st = '<style type="text/css">' +
21807                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21808                    '</style>';
21809         } else { 
21810             st = '<style type="text/css">' +
21811                     this.stylesheets +
21812                 '</style>';
21813         }
21814         
21815         st +=  '<style type="text/css">' +
21816             'IMG { cursor: pointer } ' +
21817         '</style>';
21818
21819         var cls = 'roo-htmleditor-body';
21820         
21821         if(this.bodyCls.length){
21822             cls += ' ' + this.bodyCls;
21823         }
21824         
21825         return '<html><head>' + st  +
21826             //<style type="text/css">' +
21827             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21828             //'</style>' +
21829             ' </head><body class="' +  cls + '"></body></html>';
21830     },
21831
21832     // private
21833     onRender : function(ct, position)
21834     {
21835         var _t = this;
21836         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21837         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21838         
21839         
21840         this.el.dom.style.border = '0 none';
21841         this.el.dom.setAttribute('tabIndex', -1);
21842         this.el.addClass('x-hidden hide');
21843         
21844         
21845         
21846         if(Roo.isIE){ // fix IE 1px bogus margin
21847             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21848         }
21849        
21850         
21851         this.frameId = Roo.id();
21852         
21853          
21854         
21855         var iframe = this.owner.wrap.createChild({
21856             tag: 'iframe',
21857             cls: 'form-control', // bootstrap..
21858             id: this.frameId,
21859             name: this.frameId,
21860             frameBorder : 'no',
21861             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21862         }, this.el
21863         );
21864         
21865         
21866         this.iframe = iframe.dom;
21867
21868          this.assignDocWin();
21869         
21870         this.doc.designMode = 'on';
21871        
21872         this.doc.open();
21873         this.doc.write(this.getDocMarkup());
21874         this.doc.close();
21875
21876         
21877         var task = { // must defer to wait for browser to be ready
21878             run : function(){
21879                 //console.log("run task?" + this.doc.readyState);
21880                 this.assignDocWin();
21881                 if(this.doc.body || this.doc.readyState == 'complete'){
21882                     try {
21883                         this.doc.designMode="on";
21884                     } catch (e) {
21885                         return;
21886                     }
21887                     Roo.TaskMgr.stop(task);
21888                     this.initEditor.defer(10, this);
21889                 }
21890             },
21891             interval : 10,
21892             duration: 10000,
21893             scope: this
21894         };
21895         Roo.TaskMgr.start(task);
21896
21897     },
21898
21899     // private
21900     onResize : function(w, h)
21901     {
21902          Roo.log('resize: ' +w + ',' + h );
21903         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21904         if(!this.iframe){
21905             return;
21906         }
21907         if(typeof w == 'number'){
21908             
21909             this.iframe.style.width = w + 'px';
21910         }
21911         if(typeof h == 'number'){
21912             
21913             this.iframe.style.height = h + 'px';
21914             if(this.doc){
21915                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21916             }
21917         }
21918         
21919     },
21920
21921     /**
21922      * Toggles the editor between standard and source edit mode.
21923      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21924      */
21925     toggleSourceEdit : function(sourceEditMode){
21926         
21927         this.sourceEditMode = sourceEditMode === true;
21928         
21929         if(this.sourceEditMode){
21930  
21931             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21932             
21933         }else{
21934             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21935             //this.iframe.className = '';
21936             this.deferFocus();
21937         }
21938         //this.setSize(this.owner.wrap.getSize());
21939         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21940     },
21941
21942     
21943   
21944
21945     /**
21946      * Protected method that will not generally be called directly. If you need/want
21947      * custom HTML cleanup, this is the method you should override.
21948      * @param {String} html The HTML to be cleaned
21949      * return {String} The cleaned HTML
21950      */
21951     cleanHtml : function(html){
21952         html = String(html);
21953         if(html.length > 5){
21954             if(Roo.isSafari){ // strip safari nonsense
21955                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21956             }
21957         }
21958         if(html == '&nbsp;'){
21959             html = '';
21960         }
21961         return html;
21962     },
21963
21964     /**
21965      * HTML Editor -> Textarea
21966      * Protected method that will not generally be called directly. Syncs the contents
21967      * of the editor iframe with the textarea.
21968      */
21969     syncValue : function(){
21970         if(this.initialized){
21971             var bd = (this.doc.body || this.doc.documentElement);
21972             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21973             var html = bd.innerHTML;
21974             if(Roo.isSafari){
21975                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21976                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21977                 if(m && m[1]){
21978                     html = '<div style="'+m[0]+'">' + html + '</div>';
21979                 }
21980             }
21981             html = this.cleanHtml(html);
21982             // fix up the special chars.. normaly like back quotes in word...
21983             // however we do not want to do this with chinese..
21984             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21985                 var cc = b.charCodeAt();
21986                 if (
21987                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21988                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21989                     (cc >= 0xf900 && cc < 0xfb00 )
21990                 ) {
21991                         return b;
21992                 }
21993                 return "&#"+cc+";" 
21994             });
21995             if(this.owner.fireEvent('beforesync', this, html) !== false){
21996                 this.el.dom.value = html;
21997                 this.owner.fireEvent('sync', this, html);
21998             }
21999         }
22000     },
22001
22002     /**
22003      * Protected method that will not generally be called directly. Pushes the value of the textarea
22004      * into the iframe editor.
22005      */
22006     pushValue : function(){
22007         if(this.initialized){
22008             var v = this.el.dom.value.trim();
22009             
22010 //            if(v.length < 1){
22011 //                v = '&#160;';
22012 //            }
22013             
22014             if(this.owner.fireEvent('beforepush', this, v) !== false){
22015                 var d = (this.doc.body || this.doc.documentElement);
22016                 d.innerHTML = v;
22017                 this.cleanUpPaste();
22018                 this.el.dom.value = d.innerHTML;
22019                 this.owner.fireEvent('push', this, v);
22020             }
22021         }
22022     },
22023
22024     // private
22025     deferFocus : function(){
22026         this.focus.defer(10, this);
22027     },
22028
22029     // doc'ed in Field
22030     focus : function(){
22031         if(this.win && !this.sourceEditMode){
22032             this.win.focus();
22033         }else{
22034             this.el.focus();
22035         }
22036     },
22037     
22038     assignDocWin: function()
22039     {
22040         var iframe = this.iframe;
22041         
22042          if(Roo.isIE){
22043             this.doc = iframe.contentWindow.document;
22044             this.win = iframe.contentWindow;
22045         } else {
22046 //            if (!Roo.get(this.frameId)) {
22047 //                return;
22048 //            }
22049 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22050 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22051             
22052             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22053                 return;
22054             }
22055             
22056             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22057             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22058         }
22059     },
22060     
22061     // private
22062     initEditor : function(){
22063         //console.log("INIT EDITOR");
22064         this.assignDocWin();
22065         
22066         
22067         
22068         this.doc.designMode="on";
22069         this.doc.open();
22070         this.doc.write(this.getDocMarkup());
22071         this.doc.close();
22072         
22073         var dbody = (this.doc.body || this.doc.documentElement);
22074         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22075         // this copies styles from the containing element into thsi one..
22076         // not sure why we need all of this..
22077         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22078         
22079         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22080         //ss['background-attachment'] = 'fixed'; // w3c
22081         dbody.bgProperties = 'fixed'; // ie
22082         //Roo.DomHelper.applyStyles(dbody, ss);
22083         Roo.EventManager.on(this.doc, {
22084             //'mousedown': this.onEditorEvent,
22085             'mouseup': this.onEditorEvent,
22086             'dblclick': this.onEditorEvent,
22087             'click': this.onEditorEvent,
22088             'keyup': this.onEditorEvent,
22089             buffer:100,
22090             scope: this
22091         });
22092         if(Roo.isGecko){
22093             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22094         }
22095         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22096             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22097         }
22098         this.initialized = true;
22099
22100         this.owner.fireEvent('initialize', this);
22101         this.pushValue();
22102     },
22103
22104     // private
22105     onDestroy : function(){
22106         
22107         
22108         
22109         if(this.rendered){
22110             
22111             //for (var i =0; i < this.toolbars.length;i++) {
22112             //    // fixme - ask toolbars for heights?
22113             //    this.toolbars[i].onDestroy();
22114            // }
22115             
22116             //this.wrap.dom.innerHTML = '';
22117             //this.wrap.remove();
22118         }
22119     },
22120
22121     // private
22122     onFirstFocus : function(){
22123         
22124         this.assignDocWin();
22125         
22126         
22127         this.activated = true;
22128          
22129     
22130         if(Roo.isGecko){ // prevent silly gecko errors
22131             this.win.focus();
22132             var s = this.win.getSelection();
22133             if(!s.focusNode || s.focusNode.nodeType != 3){
22134                 var r = s.getRangeAt(0);
22135                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22136                 r.collapse(true);
22137                 this.deferFocus();
22138             }
22139             try{
22140                 this.execCmd('useCSS', true);
22141                 this.execCmd('styleWithCSS', false);
22142             }catch(e){}
22143         }
22144         this.owner.fireEvent('activate', this);
22145     },
22146
22147     // private
22148     adjustFont: function(btn){
22149         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22150         //if(Roo.isSafari){ // safari
22151         //    adjust *= 2;
22152        // }
22153         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22154         if(Roo.isSafari){ // safari
22155             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22156             v =  (v < 10) ? 10 : v;
22157             v =  (v > 48) ? 48 : v;
22158             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22159             
22160         }
22161         
22162         
22163         v = Math.max(1, v+adjust);
22164         
22165         this.execCmd('FontSize', v  );
22166     },
22167
22168     onEditorEvent : function(e)
22169     {
22170         this.owner.fireEvent('editorevent', this, e);
22171       //  this.updateToolbar();
22172         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22173     },
22174
22175     insertTag : function(tg)
22176     {
22177         // could be a bit smarter... -> wrap the current selected tRoo..
22178         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22179             
22180             range = this.createRange(this.getSelection());
22181             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22182             wrappingNode.appendChild(range.extractContents());
22183             range.insertNode(wrappingNode);
22184
22185             return;
22186             
22187             
22188             
22189         }
22190         this.execCmd("formatblock",   tg);
22191         
22192     },
22193     
22194     insertText : function(txt)
22195     {
22196         
22197         
22198         var range = this.createRange();
22199         range.deleteContents();
22200                //alert(Sender.getAttribute('label'));
22201                
22202         range.insertNode(this.doc.createTextNode(txt));
22203     } ,
22204     
22205      
22206
22207     /**
22208      * Executes a Midas editor command on the editor document and performs necessary focus and
22209      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22210      * @param {String} cmd The Midas command
22211      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22212      */
22213     relayCmd : function(cmd, value){
22214         this.win.focus();
22215         this.execCmd(cmd, value);
22216         this.owner.fireEvent('editorevent', this);
22217         //this.updateToolbar();
22218         this.owner.deferFocus();
22219     },
22220
22221     /**
22222      * Executes a Midas editor command directly on the editor document.
22223      * For visual commands, you should use {@link #relayCmd} instead.
22224      * <b>This should only be called after the editor is initialized.</b>
22225      * @param {String} cmd The Midas command
22226      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22227      */
22228     execCmd : function(cmd, value){
22229         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22230         this.syncValue();
22231     },
22232  
22233  
22234    
22235     /**
22236      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22237      * to insert tRoo.
22238      * @param {String} text | dom node.. 
22239      */
22240     insertAtCursor : function(text)
22241     {
22242         
22243         if(!this.activated){
22244             return;
22245         }
22246         /*
22247         if(Roo.isIE){
22248             this.win.focus();
22249             var r = this.doc.selection.createRange();
22250             if(r){
22251                 r.collapse(true);
22252                 r.pasteHTML(text);
22253                 this.syncValue();
22254                 this.deferFocus();
22255             
22256             }
22257             return;
22258         }
22259         */
22260         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22261             this.win.focus();
22262             
22263             
22264             // from jquery ui (MIT licenced)
22265             var range, node;
22266             var win = this.win;
22267             
22268             if (win.getSelection && win.getSelection().getRangeAt) {
22269                 range = win.getSelection().getRangeAt(0);
22270                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22271                 range.insertNode(node);
22272             } else if (win.document.selection && win.document.selection.createRange) {
22273                 // no firefox support
22274                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22275                 win.document.selection.createRange().pasteHTML(txt);
22276             } else {
22277                 // no firefox support
22278                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22279                 this.execCmd('InsertHTML', txt);
22280             } 
22281             
22282             this.syncValue();
22283             
22284             this.deferFocus();
22285         }
22286     },
22287  // private
22288     mozKeyPress : function(e){
22289         if(e.ctrlKey){
22290             var c = e.getCharCode(), cmd;
22291           
22292             if(c > 0){
22293                 c = String.fromCharCode(c).toLowerCase();
22294                 switch(c){
22295                     case 'b':
22296                         cmd = 'bold';
22297                         break;
22298                     case 'i':
22299                         cmd = 'italic';
22300                         break;
22301                     
22302                     case 'u':
22303                         cmd = 'underline';
22304                         break;
22305                     
22306                     case 'v':
22307                         this.cleanUpPaste.defer(100, this);
22308                         return;
22309                         
22310                 }
22311                 if(cmd){
22312                     this.win.focus();
22313                     this.execCmd(cmd);
22314                     this.deferFocus();
22315                     e.preventDefault();
22316                 }
22317                 
22318             }
22319         }
22320     },
22321
22322     // private
22323     fixKeys : function(){ // load time branching for fastest keydown performance
22324         if(Roo.isIE){
22325             return function(e){
22326                 var k = e.getKey(), r;
22327                 if(k == e.TAB){
22328                     e.stopEvent();
22329                     r = this.doc.selection.createRange();
22330                     if(r){
22331                         r.collapse(true);
22332                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22333                         this.deferFocus();
22334                     }
22335                     return;
22336                 }
22337                 
22338                 if(k == e.ENTER){
22339                     r = this.doc.selection.createRange();
22340                     if(r){
22341                         var target = r.parentElement();
22342                         if(!target || target.tagName.toLowerCase() != 'li'){
22343                             e.stopEvent();
22344                             r.pasteHTML('<br />');
22345                             r.collapse(false);
22346                             r.select();
22347                         }
22348                     }
22349                 }
22350                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22351                     this.cleanUpPaste.defer(100, this);
22352                     return;
22353                 }
22354                 
22355                 
22356             };
22357         }else if(Roo.isOpera){
22358             return function(e){
22359                 var k = e.getKey();
22360                 if(k == e.TAB){
22361                     e.stopEvent();
22362                     this.win.focus();
22363                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22364                     this.deferFocus();
22365                 }
22366                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22367                     this.cleanUpPaste.defer(100, this);
22368                     return;
22369                 }
22370                 
22371             };
22372         }else if(Roo.isSafari){
22373             return function(e){
22374                 var k = e.getKey();
22375                 
22376                 if(k == e.TAB){
22377                     e.stopEvent();
22378                     this.execCmd('InsertText','\t');
22379                     this.deferFocus();
22380                     return;
22381                 }
22382                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22383                     this.cleanUpPaste.defer(100, this);
22384                     return;
22385                 }
22386                 
22387              };
22388         }
22389     }(),
22390     
22391     getAllAncestors: function()
22392     {
22393         var p = this.getSelectedNode();
22394         var a = [];
22395         if (!p) {
22396             a.push(p); // push blank onto stack..
22397             p = this.getParentElement();
22398         }
22399         
22400         
22401         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22402             a.push(p);
22403             p = p.parentNode;
22404         }
22405         a.push(this.doc.body);
22406         return a;
22407     },
22408     lastSel : false,
22409     lastSelNode : false,
22410     
22411     
22412     getSelection : function() 
22413     {
22414         this.assignDocWin();
22415         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22416     },
22417     
22418     getSelectedNode: function() 
22419     {
22420         // this may only work on Gecko!!!
22421         
22422         // should we cache this!!!!
22423         
22424         
22425         
22426          
22427         var range = this.createRange(this.getSelection()).cloneRange();
22428         
22429         if (Roo.isIE) {
22430             var parent = range.parentElement();
22431             while (true) {
22432                 var testRange = range.duplicate();
22433                 testRange.moveToElementText(parent);
22434                 if (testRange.inRange(range)) {
22435                     break;
22436                 }
22437                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22438                     break;
22439                 }
22440                 parent = parent.parentElement;
22441             }
22442             return parent;
22443         }
22444         
22445         // is ancestor a text element.
22446         var ac =  range.commonAncestorContainer;
22447         if (ac.nodeType == 3) {
22448             ac = ac.parentNode;
22449         }
22450         
22451         var ar = ac.childNodes;
22452          
22453         var nodes = [];
22454         var other_nodes = [];
22455         var has_other_nodes = false;
22456         for (var i=0;i<ar.length;i++) {
22457             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22458                 continue;
22459             }
22460             // fullly contained node.
22461             
22462             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22463                 nodes.push(ar[i]);
22464                 continue;
22465             }
22466             
22467             // probably selected..
22468             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22469                 other_nodes.push(ar[i]);
22470                 continue;
22471             }
22472             // outer..
22473             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22474                 continue;
22475             }
22476             
22477             
22478             has_other_nodes = true;
22479         }
22480         if (!nodes.length && other_nodes.length) {
22481             nodes= other_nodes;
22482         }
22483         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22484             return false;
22485         }
22486         
22487         return nodes[0];
22488     },
22489     createRange: function(sel)
22490     {
22491         // this has strange effects when using with 
22492         // top toolbar - not sure if it's a great idea.
22493         //this.editor.contentWindow.focus();
22494         if (typeof sel != "undefined") {
22495             try {
22496                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22497             } catch(e) {
22498                 return this.doc.createRange();
22499             }
22500         } else {
22501             return this.doc.createRange();
22502         }
22503     },
22504     getParentElement: function()
22505     {
22506         
22507         this.assignDocWin();
22508         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22509         
22510         var range = this.createRange(sel);
22511          
22512         try {
22513             var p = range.commonAncestorContainer;
22514             while (p.nodeType == 3) { // text node
22515                 p = p.parentNode;
22516             }
22517             return p;
22518         } catch (e) {
22519             return null;
22520         }
22521     
22522     },
22523     /***
22524      *
22525      * Range intersection.. the hard stuff...
22526      *  '-1' = before
22527      *  '0' = hits..
22528      *  '1' = after.
22529      *         [ -- selected range --- ]
22530      *   [fail]                        [fail]
22531      *
22532      *    basically..
22533      *      if end is before start or  hits it. fail.
22534      *      if start is after end or hits it fail.
22535      *
22536      *   if either hits (but other is outside. - then it's not 
22537      *   
22538      *    
22539      **/
22540     
22541     
22542     // @see http://www.thismuchiknow.co.uk/?p=64.
22543     rangeIntersectsNode : function(range, node)
22544     {
22545         var nodeRange = node.ownerDocument.createRange();
22546         try {
22547             nodeRange.selectNode(node);
22548         } catch (e) {
22549             nodeRange.selectNodeContents(node);
22550         }
22551     
22552         var rangeStartRange = range.cloneRange();
22553         rangeStartRange.collapse(true);
22554     
22555         var rangeEndRange = range.cloneRange();
22556         rangeEndRange.collapse(false);
22557     
22558         var nodeStartRange = nodeRange.cloneRange();
22559         nodeStartRange.collapse(true);
22560     
22561         var nodeEndRange = nodeRange.cloneRange();
22562         nodeEndRange.collapse(false);
22563     
22564         return rangeStartRange.compareBoundaryPoints(
22565                  Range.START_TO_START, nodeEndRange) == -1 &&
22566                rangeEndRange.compareBoundaryPoints(
22567                  Range.START_TO_START, nodeStartRange) == 1;
22568         
22569          
22570     },
22571     rangeCompareNode : function(range, node)
22572     {
22573         var nodeRange = node.ownerDocument.createRange();
22574         try {
22575             nodeRange.selectNode(node);
22576         } catch (e) {
22577             nodeRange.selectNodeContents(node);
22578         }
22579         
22580         
22581         range.collapse(true);
22582     
22583         nodeRange.collapse(true);
22584      
22585         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22586         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22587          
22588         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22589         
22590         var nodeIsBefore   =  ss == 1;
22591         var nodeIsAfter    = ee == -1;
22592         
22593         if (nodeIsBefore && nodeIsAfter) {
22594             return 0; // outer
22595         }
22596         if (!nodeIsBefore && nodeIsAfter) {
22597             return 1; //right trailed.
22598         }
22599         
22600         if (nodeIsBefore && !nodeIsAfter) {
22601             return 2;  // left trailed.
22602         }
22603         // fully contined.
22604         return 3;
22605     },
22606
22607     // private? - in a new class?
22608     cleanUpPaste :  function()
22609     {
22610         // cleans up the whole document..
22611         Roo.log('cleanuppaste');
22612         
22613         this.cleanUpChildren(this.doc.body);
22614         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22615         if (clean != this.doc.body.innerHTML) {
22616             this.doc.body.innerHTML = clean;
22617         }
22618         
22619     },
22620     
22621     cleanWordChars : function(input) {// change the chars to hex code
22622         var he = Roo.HtmlEditorCore;
22623         
22624         var output = input;
22625         Roo.each(he.swapCodes, function(sw) { 
22626             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22627             
22628             output = output.replace(swapper, sw[1]);
22629         });
22630         
22631         return output;
22632     },
22633     
22634     
22635     cleanUpChildren : function (n)
22636     {
22637         if (!n.childNodes.length) {
22638             return;
22639         }
22640         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22641            this.cleanUpChild(n.childNodes[i]);
22642         }
22643     },
22644     
22645     
22646         
22647     
22648     cleanUpChild : function (node)
22649     {
22650         var ed = this;
22651         //console.log(node);
22652         if (node.nodeName == "#text") {
22653             // clean up silly Windows -- stuff?
22654             return; 
22655         }
22656         if (node.nodeName == "#comment") {
22657             node.parentNode.removeChild(node);
22658             // clean up silly Windows -- stuff?
22659             return; 
22660         }
22661         var lcname = node.tagName.toLowerCase();
22662         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22663         // whitelist of tags..
22664         
22665         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22666             // remove node.
22667             node.parentNode.removeChild(node);
22668             return;
22669             
22670         }
22671         
22672         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22673         
22674         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22675         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22676         
22677         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22678         //    remove_keep_children = true;
22679         //}
22680         
22681         if (remove_keep_children) {
22682             this.cleanUpChildren(node);
22683             // inserts everything just before this node...
22684             while (node.childNodes.length) {
22685                 var cn = node.childNodes[0];
22686                 node.removeChild(cn);
22687                 node.parentNode.insertBefore(cn, node);
22688             }
22689             node.parentNode.removeChild(node);
22690             return;
22691         }
22692         
22693         if (!node.attributes || !node.attributes.length) {
22694             this.cleanUpChildren(node);
22695             return;
22696         }
22697         
22698         function cleanAttr(n,v)
22699         {
22700             
22701             if (v.match(/^\./) || v.match(/^\//)) {
22702                 return;
22703             }
22704             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22705                 return;
22706             }
22707             if (v.match(/^#/)) {
22708                 return;
22709             }
22710 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22711             node.removeAttribute(n);
22712             
22713         }
22714         
22715         var cwhite = this.cwhite;
22716         var cblack = this.cblack;
22717             
22718         function cleanStyle(n,v)
22719         {
22720             if (v.match(/expression/)) { //XSS?? should we even bother..
22721                 node.removeAttribute(n);
22722                 return;
22723             }
22724             
22725             var parts = v.split(/;/);
22726             var clean = [];
22727             
22728             Roo.each(parts, function(p) {
22729                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22730                 if (!p.length) {
22731                     return true;
22732                 }
22733                 var l = p.split(':').shift().replace(/\s+/g,'');
22734                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22735                 
22736                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22737 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22738                     //node.removeAttribute(n);
22739                     return true;
22740                 }
22741                 //Roo.log()
22742                 // only allow 'c whitelisted system attributes'
22743                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22744 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22745                     //node.removeAttribute(n);
22746                     return true;
22747                 }
22748                 
22749                 
22750                  
22751                 
22752                 clean.push(p);
22753                 return true;
22754             });
22755             if (clean.length) { 
22756                 node.setAttribute(n, clean.join(';'));
22757             } else {
22758                 node.removeAttribute(n);
22759             }
22760             
22761         }
22762         
22763         
22764         for (var i = node.attributes.length-1; i > -1 ; i--) {
22765             var a = node.attributes[i];
22766             //console.log(a);
22767             
22768             if (a.name.toLowerCase().substr(0,2)=='on')  {
22769                 node.removeAttribute(a.name);
22770                 continue;
22771             }
22772             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22773                 node.removeAttribute(a.name);
22774                 continue;
22775             }
22776             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22777                 cleanAttr(a.name,a.value); // fixme..
22778                 continue;
22779             }
22780             if (a.name == 'style') {
22781                 cleanStyle(a.name,a.value);
22782                 continue;
22783             }
22784             /// clean up MS crap..
22785             // tecnically this should be a list of valid class'es..
22786             
22787             
22788             if (a.name == 'class') {
22789                 if (a.value.match(/^Mso/)) {
22790                     node.className = '';
22791                 }
22792                 
22793                 if (a.value.match(/^body$/)) {
22794                     node.className = '';
22795                 }
22796                 continue;
22797             }
22798             
22799             // style cleanup!?
22800             // class cleanup?
22801             
22802         }
22803         
22804         
22805         this.cleanUpChildren(node);
22806         
22807         
22808     },
22809     
22810     /**
22811      * Clean up MS wordisms...
22812      */
22813     cleanWord : function(node)
22814     {
22815         
22816         
22817         if (!node) {
22818             this.cleanWord(this.doc.body);
22819             return;
22820         }
22821         if (node.nodeName == "#text") {
22822             // clean up silly Windows -- stuff?
22823             return; 
22824         }
22825         if (node.nodeName == "#comment") {
22826             node.parentNode.removeChild(node);
22827             // clean up silly Windows -- stuff?
22828             return; 
22829         }
22830         
22831         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22832             node.parentNode.removeChild(node);
22833             return;
22834         }
22835         
22836         // remove - but keep children..
22837         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22838             while (node.childNodes.length) {
22839                 var cn = node.childNodes[0];
22840                 node.removeChild(cn);
22841                 node.parentNode.insertBefore(cn, node);
22842             }
22843             node.parentNode.removeChild(node);
22844             this.iterateChildren(node, this.cleanWord);
22845             return;
22846         }
22847         // clean styles
22848         if (node.className.length) {
22849             
22850             var cn = node.className.split(/\W+/);
22851             var cna = [];
22852             Roo.each(cn, function(cls) {
22853                 if (cls.match(/Mso[a-zA-Z]+/)) {
22854                     return;
22855                 }
22856                 cna.push(cls);
22857             });
22858             node.className = cna.length ? cna.join(' ') : '';
22859             if (!cna.length) {
22860                 node.removeAttribute("class");
22861             }
22862         }
22863         
22864         if (node.hasAttribute("lang")) {
22865             node.removeAttribute("lang");
22866         }
22867         
22868         if (node.hasAttribute("style")) {
22869             
22870             var styles = node.getAttribute("style").split(";");
22871             var nstyle = [];
22872             Roo.each(styles, function(s) {
22873                 if (!s.match(/:/)) {
22874                     return;
22875                 }
22876                 var kv = s.split(":");
22877                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22878                     return;
22879                 }
22880                 // what ever is left... we allow.
22881                 nstyle.push(s);
22882             });
22883             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22884             if (!nstyle.length) {
22885                 node.removeAttribute('style');
22886             }
22887         }
22888         this.iterateChildren(node, this.cleanWord);
22889         
22890         
22891         
22892     },
22893     /**
22894      * iterateChildren of a Node, calling fn each time, using this as the scole..
22895      * @param {DomNode} node node to iterate children of.
22896      * @param {Function} fn method of this class to call on each item.
22897      */
22898     iterateChildren : function(node, fn)
22899     {
22900         if (!node.childNodes.length) {
22901                 return;
22902         }
22903         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22904            fn.call(this, node.childNodes[i])
22905         }
22906     },
22907     
22908     
22909     /**
22910      * cleanTableWidths.
22911      *
22912      * Quite often pasting from word etc.. results in tables with column and widths.
22913      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22914      *
22915      */
22916     cleanTableWidths : function(node)
22917     {
22918          
22919          
22920         if (!node) {
22921             this.cleanTableWidths(this.doc.body);
22922             return;
22923         }
22924         
22925         // ignore list...
22926         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22927             return; 
22928         }
22929         Roo.log(node.tagName);
22930         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22931             this.iterateChildren(node, this.cleanTableWidths);
22932             return;
22933         }
22934         if (node.hasAttribute('width')) {
22935             node.removeAttribute('width');
22936         }
22937         
22938          
22939         if (node.hasAttribute("style")) {
22940             // pretty basic...
22941             
22942             var styles = node.getAttribute("style").split(";");
22943             var nstyle = [];
22944             Roo.each(styles, function(s) {
22945                 if (!s.match(/:/)) {
22946                     return;
22947                 }
22948                 var kv = s.split(":");
22949                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22950                     return;
22951                 }
22952                 // what ever is left... we allow.
22953                 nstyle.push(s);
22954             });
22955             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22956             if (!nstyle.length) {
22957                 node.removeAttribute('style');
22958             }
22959         }
22960         
22961         this.iterateChildren(node, this.cleanTableWidths);
22962         
22963         
22964     },
22965     
22966     
22967     
22968     
22969     domToHTML : function(currentElement, depth, nopadtext) {
22970         
22971         depth = depth || 0;
22972         nopadtext = nopadtext || false;
22973     
22974         if (!currentElement) {
22975             return this.domToHTML(this.doc.body);
22976         }
22977         
22978         //Roo.log(currentElement);
22979         var j;
22980         var allText = false;
22981         var nodeName = currentElement.nodeName;
22982         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22983         
22984         if  (nodeName == '#text') {
22985             
22986             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22987         }
22988         
22989         
22990         var ret = '';
22991         if (nodeName != 'BODY') {
22992              
22993             var i = 0;
22994             // Prints the node tagName, such as <A>, <IMG>, etc
22995             if (tagName) {
22996                 var attr = [];
22997                 for(i = 0; i < currentElement.attributes.length;i++) {
22998                     // quoting?
22999                     var aname = currentElement.attributes.item(i).name;
23000                     if (!currentElement.attributes.item(i).value.length) {
23001                         continue;
23002                     }
23003                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23004                 }
23005                 
23006                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23007             } 
23008             else {
23009                 
23010                 // eack
23011             }
23012         } else {
23013             tagName = false;
23014         }
23015         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23016             return ret;
23017         }
23018         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23019             nopadtext = true;
23020         }
23021         
23022         
23023         // Traverse the tree
23024         i = 0;
23025         var currentElementChild = currentElement.childNodes.item(i);
23026         var allText = true;
23027         var innerHTML  = '';
23028         lastnode = '';
23029         while (currentElementChild) {
23030             // Formatting code (indent the tree so it looks nice on the screen)
23031             var nopad = nopadtext;
23032             if (lastnode == 'SPAN') {
23033                 nopad  = true;
23034             }
23035             // text
23036             if  (currentElementChild.nodeName == '#text') {
23037                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23038                 toadd = nopadtext ? toadd : toadd.trim();
23039                 if (!nopad && toadd.length > 80) {
23040                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23041                 }
23042                 innerHTML  += toadd;
23043                 
23044                 i++;
23045                 currentElementChild = currentElement.childNodes.item(i);
23046                 lastNode = '';
23047                 continue;
23048             }
23049             allText = false;
23050             
23051             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23052                 
23053             // Recursively traverse the tree structure of the child node
23054             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23055             lastnode = currentElementChild.nodeName;
23056             i++;
23057             currentElementChild=currentElement.childNodes.item(i);
23058         }
23059         
23060         ret += innerHTML;
23061         
23062         if (!allText) {
23063                 // The remaining code is mostly for formatting the tree
23064             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23065         }
23066         
23067         
23068         if (tagName) {
23069             ret+= "</"+tagName+">";
23070         }
23071         return ret;
23072         
23073     },
23074         
23075     applyBlacklists : function()
23076     {
23077         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23078         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23079         
23080         this.white = [];
23081         this.black = [];
23082         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23083             if (b.indexOf(tag) > -1) {
23084                 return;
23085             }
23086             this.white.push(tag);
23087             
23088         }, this);
23089         
23090         Roo.each(w, function(tag) {
23091             if (b.indexOf(tag) > -1) {
23092                 return;
23093             }
23094             if (this.white.indexOf(tag) > -1) {
23095                 return;
23096             }
23097             this.white.push(tag);
23098             
23099         }, this);
23100         
23101         
23102         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23103             if (w.indexOf(tag) > -1) {
23104                 return;
23105             }
23106             this.black.push(tag);
23107             
23108         }, this);
23109         
23110         Roo.each(b, function(tag) {
23111             if (w.indexOf(tag) > -1) {
23112                 return;
23113             }
23114             if (this.black.indexOf(tag) > -1) {
23115                 return;
23116             }
23117             this.black.push(tag);
23118             
23119         }, this);
23120         
23121         
23122         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23123         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23124         
23125         this.cwhite = [];
23126         this.cblack = [];
23127         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23128             if (b.indexOf(tag) > -1) {
23129                 return;
23130             }
23131             this.cwhite.push(tag);
23132             
23133         }, this);
23134         
23135         Roo.each(w, function(tag) {
23136             if (b.indexOf(tag) > -1) {
23137                 return;
23138             }
23139             if (this.cwhite.indexOf(tag) > -1) {
23140                 return;
23141             }
23142             this.cwhite.push(tag);
23143             
23144         }, this);
23145         
23146         
23147         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23148             if (w.indexOf(tag) > -1) {
23149                 return;
23150             }
23151             this.cblack.push(tag);
23152             
23153         }, this);
23154         
23155         Roo.each(b, function(tag) {
23156             if (w.indexOf(tag) > -1) {
23157                 return;
23158             }
23159             if (this.cblack.indexOf(tag) > -1) {
23160                 return;
23161             }
23162             this.cblack.push(tag);
23163             
23164         }, this);
23165     },
23166     
23167     setStylesheets : function(stylesheets)
23168     {
23169         if(typeof(stylesheets) == 'string'){
23170             Roo.get(this.iframe.contentDocument.head).createChild({
23171                 tag : 'link',
23172                 rel : 'stylesheet',
23173                 type : 'text/css',
23174                 href : stylesheets
23175             });
23176             
23177             return;
23178         }
23179         var _this = this;
23180      
23181         Roo.each(stylesheets, function(s) {
23182             if(!s.length){
23183                 return;
23184             }
23185             
23186             Roo.get(_this.iframe.contentDocument.head).createChild({
23187                 tag : 'link',
23188                 rel : 'stylesheet',
23189                 type : 'text/css',
23190                 href : s
23191             });
23192         });
23193
23194         
23195     },
23196     
23197     removeStylesheets : function()
23198     {
23199         var _this = this;
23200         
23201         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23202             s.remove();
23203         });
23204     },
23205     
23206     setStyle : function(style)
23207     {
23208         Roo.get(this.iframe.contentDocument.head).createChild({
23209             tag : 'style',
23210             type : 'text/css',
23211             html : style
23212         });
23213
23214         return;
23215     }
23216     
23217     // hide stuff that is not compatible
23218     /**
23219      * @event blur
23220      * @hide
23221      */
23222     /**
23223      * @event change
23224      * @hide
23225      */
23226     /**
23227      * @event focus
23228      * @hide
23229      */
23230     /**
23231      * @event specialkey
23232      * @hide
23233      */
23234     /**
23235      * @cfg {String} fieldClass @hide
23236      */
23237     /**
23238      * @cfg {String} focusClass @hide
23239      */
23240     /**
23241      * @cfg {String} autoCreate @hide
23242      */
23243     /**
23244      * @cfg {String} inputType @hide
23245      */
23246     /**
23247      * @cfg {String} invalidClass @hide
23248      */
23249     /**
23250      * @cfg {String} invalidText @hide
23251      */
23252     /**
23253      * @cfg {String} msgFx @hide
23254      */
23255     /**
23256      * @cfg {String} validateOnBlur @hide
23257      */
23258 });
23259
23260 Roo.HtmlEditorCore.white = [
23261         'area', 'br', 'img', 'input', 'hr', 'wbr',
23262         
23263        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23264        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23265        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23266        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23267        'table',   'ul',         'xmp', 
23268        
23269        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23270       'thead',   'tr', 
23271      
23272       'dir', 'menu', 'ol', 'ul', 'dl',
23273        
23274       'embed',  'object'
23275 ];
23276
23277
23278 Roo.HtmlEditorCore.black = [
23279     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23280         'applet', // 
23281         'base',   'basefont', 'bgsound', 'blink',  'body', 
23282         'frame',  'frameset', 'head',    'html',   'ilayer', 
23283         'iframe', 'layer',  'link',     'meta',    'object',   
23284         'script', 'style' ,'title',  'xml' // clean later..
23285 ];
23286 Roo.HtmlEditorCore.clean = [
23287     'script', 'style', 'title', 'xml'
23288 ];
23289 Roo.HtmlEditorCore.remove = [
23290     'font'
23291 ];
23292 // attributes..
23293
23294 Roo.HtmlEditorCore.ablack = [
23295     'on'
23296 ];
23297     
23298 Roo.HtmlEditorCore.aclean = [ 
23299     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23300 ];
23301
23302 // protocols..
23303 Roo.HtmlEditorCore.pwhite= [
23304         'http',  'https',  'mailto'
23305 ];
23306
23307 // white listed style attributes.
23308 Roo.HtmlEditorCore.cwhite= [
23309       //  'text-align', /// default is to allow most things..
23310       
23311          
23312 //        'font-size'//??
23313 ];
23314
23315 // black listed style attributes.
23316 Roo.HtmlEditorCore.cblack= [
23317       //  'font-size' -- this can be set by the project 
23318 ];
23319
23320
23321 Roo.HtmlEditorCore.swapCodes   =[ 
23322     [    8211, "--" ], 
23323     [    8212, "--" ], 
23324     [    8216,  "'" ],  
23325     [    8217, "'" ],  
23326     [    8220, '"' ],  
23327     [    8221, '"' ],  
23328     [    8226, "*" ],  
23329     [    8230, "..." ]
23330 ]; 
23331
23332     /*
23333  * - LGPL
23334  *
23335  * HtmlEditor
23336  * 
23337  */
23338
23339 /**
23340  * @class Roo.bootstrap.HtmlEditor
23341  * @extends Roo.bootstrap.TextArea
23342  * Bootstrap HtmlEditor class
23343
23344  * @constructor
23345  * Create a new HtmlEditor
23346  * @param {Object} config The config object
23347  */
23348
23349 Roo.bootstrap.HtmlEditor = function(config){
23350     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23351     if (!this.toolbars) {
23352         this.toolbars = [];
23353     }
23354     
23355     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23356     this.addEvents({
23357             /**
23358              * @event initialize
23359              * Fires when the editor is fully initialized (including the iframe)
23360              * @param {HtmlEditor} this
23361              */
23362             initialize: true,
23363             /**
23364              * @event activate
23365              * Fires when the editor is first receives the focus. Any insertion must wait
23366              * until after this event.
23367              * @param {HtmlEditor} this
23368              */
23369             activate: true,
23370              /**
23371              * @event beforesync
23372              * Fires before the textarea is updated with content from the editor iframe. Return false
23373              * to cancel the sync.
23374              * @param {HtmlEditor} this
23375              * @param {String} html
23376              */
23377             beforesync: true,
23378              /**
23379              * @event beforepush
23380              * Fires before the iframe editor is updated with content from the textarea. Return false
23381              * to cancel the push.
23382              * @param {HtmlEditor} this
23383              * @param {String} html
23384              */
23385             beforepush: true,
23386              /**
23387              * @event sync
23388              * Fires when the textarea is updated with content from the editor iframe.
23389              * @param {HtmlEditor} this
23390              * @param {String} html
23391              */
23392             sync: true,
23393              /**
23394              * @event push
23395              * Fires when the iframe editor is updated with content from the textarea.
23396              * @param {HtmlEditor} this
23397              * @param {String} html
23398              */
23399             push: true,
23400              /**
23401              * @event editmodechange
23402              * Fires when the editor switches edit modes
23403              * @param {HtmlEditor} this
23404              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23405              */
23406             editmodechange: true,
23407             /**
23408              * @event editorevent
23409              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23410              * @param {HtmlEditor} this
23411              */
23412             editorevent: true,
23413             /**
23414              * @event firstfocus
23415              * Fires when on first focus - needed by toolbars..
23416              * @param {HtmlEditor} this
23417              */
23418             firstfocus: true,
23419             /**
23420              * @event autosave
23421              * Auto save the htmlEditor value as a file into Events
23422              * @param {HtmlEditor} this
23423              */
23424             autosave: true,
23425             /**
23426              * @event savedpreview
23427              * preview the saved version of htmlEditor
23428              * @param {HtmlEditor} this
23429              */
23430             savedpreview: true
23431         });
23432 };
23433
23434
23435 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23436     
23437     
23438       /**
23439      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23440      */
23441     toolbars : false,
23442     
23443      /**
23444     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23445     */
23446     btns : [],
23447    
23448      /**
23449      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23450      *                        Roo.resizable.
23451      */
23452     resizable : false,
23453      /**
23454      * @cfg {Number} height (in pixels)
23455      */   
23456     height: 300,
23457    /**
23458      * @cfg {Number} width (in pixels)
23459      */   
23460     width: false,
23461     
23462     /**
23463      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23464      * 
23465      */
23466     stylesheets: false,
23467     
23468     // id of frame..
23469     frameId: false,
23470     
23471     // private properties
23472     validationEvent : false,
23473     deferHeight: true,
23474     initialized : false,
23475     activated : false,
23476     
23477     onFocus : Roo.emptyFn,
23478     iframePad:3,
23479     hideMode:'offsets',
23480     
23481     tbContainer : false,
23482     
23483     bodyCls : '',
23484     
23485     toolbarContainer :function() {
23486         return this.wrap.select('.x-html-editor-tb',true).first();
23487     },
23488
23489     /**
23490      * Protected method that will not generally be called directly. It
23491      * is called when the editor creates its toolbar. Override this method if you need to
23492      * add custom toolbar buttons.
23493      * @param {HtmlEditor} editor
23494      */
23495     createToolbar : function(){
23496         Roo.log('renewing');
23497         Roo.log("create toolbars");
23498         
23499         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23500         this.toolbars[0].render(this.toolbarContainer());
23501         
23502         return;
23503         
23504 //        if (!editor.toolbars || !editor.toolbars.length) {
23505 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23506 //        }
23507 //        
23508 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23509 //            editor.toolbars[i] = Roo.factory(
23510 //                    typeof(editor.toolbars[i]) == 'string' ?
23511 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23512 //                Roo.bootstrap.HtmlEditor);
23513 //            editor.toolbars[i].init(editor);
23514 //        }
23515     },
23516
23517      
23518     // private
23519     onRender : function(ct, position)
23520     {
23521        // Roo.log("Call onRender: " + this.xtype);
23522         var _t = this;
23523         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23524       
23525         this.wrap = this.inputEl().wrap({
23526             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23527         });
23528         
23529         this.editorcore.onRender(ct, position);
23530          
23531         if (this.resizable) {
23532             this.resizeEl = new Roo.Resizable(this.wrap, {
23533                 pinned : true,
23534                 wrap: true,
23535                 dynamic : true,
23536                 minHeight : this.height,
23537                 height: this.height,
23538                 handles : this.resizable,
23539                 width: this.width,
23540                 listeners : {
23541                     resize : function(r, w, h) {
23542                         _t.onResize(w,h); // -something
23543                     }
23544                 }
23545             });
23546             
23547         }
23548         this.createToolbar(this);
23549        
23550         
23551         if(!this.width && this.resizable){
23552             this.setSize(this.wrap.getSize());
23553         }
23554         if (this.resizeEl) {
23555             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23556             // should trigger onReize..
23557         }
23558         
23559     },
23560
23561     // private
23562     onResize : function(w, h)
23563     {
23564         Roo.log('resize: ' +w + ',' + h );
23565         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23566         var ew = false;
23567         var eh = false;
23568         
23569         if(this.inputEl() ){
23570             if(typeof w == 'number'){
23571                 var aw = w - this.wrap.getFrameWidth('lr');
23572                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23573                 ew = aw;
23574             }
23575             if(typeof h == 'number'){
23576                  var tbh = -11;  // fixme it needs to tool bar size!
23577                 for (var i =0; i < this.toolbars.length;i++) {
23578                     // fixme - ask toolbars for heights?
23579                     tbh += this.toolbars[i].el.getHeight();
23580                     //if (this.toolbars[i].footer) {
23581                     //    tbh += this.toolbars[i].footer.el.getHeight();
23582                     //}
23583                 }
23584               
23585                 
23586                 
23587                 
23588                 
23589                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23590                 ah -= 5; // knock a few pixes off for look..
23591                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23592                 var eh = ah;
23593             }
23594         }
23595         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23596         this.editorcore.onResize(ew,eh);
23597         
23598     },
23599
23600     /**
23601      * Toggles the editor between standard and source edit mode.
23602      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23603      */
23604     toggleSourceEdit : function(sourceEditMode)
23605     {
23606         this.editorcore.toggleSourceEdit(sourceEditMode);
23607         
23608         if(this.editorcore.sourceEditMode){
23609             Roo.log('editor - showing textarea');
23610             
23611 //            Roo.log('in');
23612 //            Roo.log(this.syncValue());
23613             this.syncValue();
23614             this.inputEl().removeClass(['hide', 'x-hidden']);
23615             this.inputEl().dom.removeAttribute('tabIndex');
23616             this.inputEl().focus();
23617         }else{
23618             Roo.log('editor - hiding textarea');
23619 //            Roo.log('out')
23620 //            Roo.log(this.pushValue()); 
23621             this.pushValue();
23622             
23623             this.inputEl().addClass(['hide', 'x-hidden']);
23624             this.inputEl().dom.setAttribute('tabIndex', -1);
23625             //this.deferFocus();
23626         }
23627          
23628         if(this.resizable){
23629             this.setSize(this.wrap.getSize());
23630         }
23631         
23632         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23633     },
23634  
23635     // private (for BoxComponent)
23636     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23637
23638     // private (for BoxComponent)
23639     getResizeEl : function(){
23640         return this.wrap;
23641     },
23642
23643     // private (for BoxComponent)
23644     getPositionEl : function(){
23645         return this.wrap;
23646     },
23647
23648     // private
23649     initEvents : function(){
23650         this.originalValue = this.getValue();
23651     },
23652
23653 //    /**
23654 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23655 //     * @method
23656 //     */
23657 //    markInvalid : Roo.emptyFn,
23658 //    /**
23659 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23660 //     * @method
23661 //     */
23662 //    clearInvalid : Roo.emptyFn,
23663
23664     setValue : function(v){
23665         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23666         this.editorcore.pushValue();
23667     },
23668
23669      
23670     // private
23671     deferFocus : function(){
23672         this.focus.defer(10, this);
23673     },
23674
23675     // doc'ed in Field
23676     focus : function(){
23677         this.editorcore.focus();
23678         
23679     },
23680       
23681
23682     // private
23683     onDestroy : function(){
23684         
23685         
23686         
23687         if(this.rendered){
23688             
23689             for (var i =0; i < this.toolbars.length;i++) {
23690                 // fixme - ask toolbars for heights?
23691                 this.toolbars[i].onDestroy();
23692             }
23693             
23694             this.wrap.dom.innerHTML = '';
23695             this.wrap.remove();
23696         }
23697     },
23698
23699     // private
23700     onFirstFocus : function(){
23701         //Roo.log("onFirstFocus");
23702         this.editorcore.onFirstFocus();
23703          for (var i =0; i < this.toolbars.length;i++) {
23704             this.toolbars[i].onFirstFocus();
23705         }
23706         
23707     },
23708     
23709     // private
23710     syncValue : function()
23711     {   
23712         this.editorcore.syncValue();
23713     },
23714     
23715     pushValue : function()
23716     {   
23717         this.editorcore.pushValue();
23718     }
23719      
23720     
23721     // hide stuff that is not compatible
23722     /**
23723      * @event blur
23724      * @hide
23725      */
23726     /**
23727      * @event change
23728      * @hide
23729      */
23730     /**
23731      * @event focus
23732      * @hide
23733      */
23734     /**
23735      * @event specialkey
23736      * @hide
23737      */
23738     /**
23739      * @cfg {String} fieldClass @hide
23740      */
23741     /**
23742      * @cfg {String} focusClass @hide
23743      */
23744     /**
23745      * @cfg {String} autoCreate @hide
23746      */
23747     /**
23748      * @cfg {String} inputType @hide
23749      */
23750     /**
23751      * @cfg {String} invalidClass @hide
23752      */
23753     /**
23754      * @cfg {String} invalidText @hide
23755      */
23756     /**
23757      * @cfg {String} msgFx @hide
23758      */
23759     /**
23760      * @cfg {String} validateOnBlur @hide
23761      */
23762 });
23763  
23764     
23765    
23766    
23767    
23768       
23769 Roo.namespace('Roo.bootstrap.htmleditor');
23770 /**
23771  * @class Roo.bootstrap.HtmlEditorToolbar1
23772  * Basic Toolbar
23773  * 
23774  * Usage:
23775  *
23776  new Roo.bootstrap.HtmlEditor({
23777     ....
23778     toolbars : [
23779         new Roo.bootstrap.HtmlEditorToolbar1({
23780             disable : { fonts: 1 , format: 1, ..., ... , ...],
23781             btns : [ .... ]
23782         })
23783     }
23784      
23785  * 
23786  * @cfg {Object} disable List of elements to disable..
23787  * @cfg {Array} btns List of additional buttons.
23788  * 
23789  * 
23790  * NEEDS Extra CSS? 
23791  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23792  */
23793  
23794 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23795 {
23796     
23797     Roo.apply(this, config);
23798     
23799     // default disabled, based on 'good practice'..
23800     this.disable = this.disable || {};
23801     Roo.applyIf(this.disable, {
23802         fontSize : true,
23803         colors : true,
23804         specialElements : true
23805     });
23806     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23807     
23808     this.editor = config.editor;
23809     this.editorcore = config.editor.editorcore;
23810     
23811     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23812     
23813     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23814     // dont call parent... till later.
23815 }
23816 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23817      
23818     bar : true,
23819     
23820     editor : false,
23821     editorcore : false,
23822     
23823     
23824     formats : [
23825         "p" ,  
23826         "h1","h2","h3","h4","h5","h6", 
23827         "pre", "code", 
23828         "abbr", "acronym", "address", "cite", "samp", "var",
23829         'div','span'
23830     ],
23831     
23832     onRender : function(ct, position)
23833     {
23834        // Roo.log("Call onRender: " + this.xtype);
23835         
23836        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23837        Roo.log(this.el);
23838        this.el.dom.style.marginBottom = '0';
23839        var _this = this;
23840        var editorcore = this.editorcore;
23841        var editor= this.editor;
23842        
23843        var children = [];
23844        var btn = function(id,cmd , toggle, handler, html){
23845        
23846             var  event = toggle ? 'toggle' : 'click';
23847        
23848             var a = {
23849                 size : 'sm',
23850                 xtype: 'Button',
23851                 xns: Roo.bootstrap,
23852                 glyphicon : id,
23853                 cmd : id || cmd,
23854                 enableToggle:toggle !== false,
23855                 html : html || '',
23856                 pressed : toggle ? false : null,
23857                 listeners : {}
23858             };
23859             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23860                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23861             };
23862             children.push(a);
23863             return a;
23864        }
23865        
23866     //    var cb_box = function...
23867         
23868         var style = {
23869                 xtype: 'Button',
23870                 size : 'sm',
23871                 xns: Roo.bootstrap,
23872                 glyphicon : 'font',
23873                 //html : 'submit'
23874                 menu : {
23875                     xtype: 'Menu',
23876                     xns: Roo.bootstrap,
23877                     items:  []
23878                 }
23879         };
23880         Roo.each(this.formats, function(f) {
23881             style.menu.items.push({
23882                 xtype :'MenuItem',
23883                 xns: Roo.bootstrap,
23884                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23885                 tagname : f,
23886                 listeners : {
23887                     click : function()
23888                     {
23889                         editorcore.insertTag(this.tagname);
23890                         editor.focus();
23891                     }
23892                 }
23893                 
23894             });
23895         });
23896         children.push(style);   
23897         
23898         btn('bold',false,true);
23899         btn('italic',false,true);
23900         btn('align-left', 'justifyleft',true);
23901         btn('align-center', 'justifycenter',true);
23902         btn('align-right' , 'justifyright',true);
23903         btn('link', false, false, function(btn) {
23904             //Roo.log("create link?");
23905             var url = prompt(this.createLinkText, this.defaultLinkValue);
23906             if(url && url != 'http:/'+'/'){
23907                 this.editorcore.relayCmd('createlink', url);
23908             }
23909         }),
23910         btn('list','insertunorderedlist',true);
23911         btn('pencil', false,true, function(btn){
23912                 Roo.log(this);
23913                 this.toggleSourceEdit(btn.pressed);
23914         });
23915         
23916         if (this.editor.btns.length > 0) {
23917             for (var i = 0; i<this.editor.btns.length; i++) {
23918                 children.push(this.editor.btns[i]);
23919             }
23920         }
23921         
23922         /*
23923         var cog = {
23924                 xtype: 'Button',
23925                 size : 'sm',
23926                 xns: Roo.bootstrap,
23927                 glyphicon : 'cog',
23928                 //html : 'submit'
23929                 menu : {
23930                     xtype: 'Menu',
23931                     xns: Roo.bootstrap,
23932                     items:  []
23933                 }
23934         };
23935         
23936         cog.menu.items.push({
23937             xtype :'MenuItem',
23938             xns: Roo.bootstrap,
23939             html : Clean styles,
23940             tagname : f,
23941             listeners : {
23942                 click : function()
23943                 {
23944                     editorcore.insertTag(this.tagname);
23945                     editor.focus();
23946                 }
23947             }
23948             
23949         });
23950        */
23951         
23952          
23953        this.xtype = 'NavSimplebar';
23954         
23955         for(var i=0;i< children.length;i++) {
23956             
23957             this.buttons.add(this.addxtypeChild(children[i]));
23958             
23959         }
23960         
23961         editor.on('editorevent', this.updateToolbar, this);
23962     },
23963     onBtnClick : function(id)
23964     {
23965        this.editorcore.relayCmd(id);
23966        this.editorcore.focus();
23967     },
23968     
23969     /**
23970      * Protected method that will not generally be called directly. It triggers
23971      * a toolbar update by reading the markup state of the current selection in the editor.
23972      */
23973     updateToolbar: function(){
23974
23975         if(!this.editorcore.activated){
23976             this.editor.onFirstFocus(); // is this neeed?
23977             return;
23978         }
23979
23980         var btns = this.buttons; 
23981         var doc = this.editorcore.doc;
23982         btns.get('bold').setActive(doc.queryCommandState('bold'));
23983         btns.get('italic').setActive(doc.queryCommandState('italic'));
23984         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23985         
23986         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23987         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23988         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23989         
23990         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23991         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23992          /*
23993         
23994         var ans = this.editorcore.getAllAncestors();
23995         if (this.formatCombo) {
23996             
23997             
23998             var store = this.formatCombo.store;
23999             this.formatCombo.setValue("");
24000             for (var i =0; i < ans.length;i++) {
24001                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24002                     // select it..
24003                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24004                     break;
24005                 }
24006             }
24007         }
24008         
24009         
24010         
24011         // hides menus... - so this cant be on a menu...
24012         Roo.bootstrap.MenuMgr.hideAll();
24013         */
24014         Roo.bootstrap.MenuMgr.hideAll();
24015         //this.editorsyncValue();
24016     },
24017     onFirstFocus: function() {
24018         this.buttons.each(function(item){
24019            item.enable();
24020         });
24021     },
24022     toggleSourceEdit : function(sourceEditMode){
24023         
24024           
24025         if(sourceEditMode){
24026             Roo.log("disabling buttons");
24027            this.buttons.each( function(item){
24028                 if(item.cmd != 'pencil'){
24029                     item.disable();
24030                 }
24031             });
24032           
24033         }else{
24034             Roo.log("enabling buttons");
24035             if(this.editorcore.initialized){
24036                 this.buttons.each( function(item){
24037                     item.enable();
24038                 });
24039             }
24040             
24041         }
24042         Roo.log("calling toggole on editor");
24043         // tell the editor that it's been pressed..
24044         this.editor.toggleSourceEdit(sourceEditMode);
24045        
24046     }
24047 });
24048
24049
24050
24051
24052
24053 /**
24054  * @class Roo.bootstrap.Table.AbstractSelectionModel
24055  * @extends Roo.util.Observable
24056  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24057  * implemented by descendant classes.  This class should not be directly instantiated.
24058  * @constructor
24059  */
24060 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24061     this.locked = false;
24062     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24063 };
24064
24065
24066 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24067     /** @ignore Called by the grid automatically. Do not call directly. */
24068     init : function(grid){
24069         this.grid = grid;
24070         this.initEvents();
24071     },
24072
24073     /**
24074      * Locks the selections.
24075      */
24076     lock : function(){
24077         this.locked = true;
24078     },
24079
24080     /**
24081      * Unlocks the selections.
24082      */
24083     unlock : function(){
24084         this.locked = false;
24085     },
24086
24087     /**
24088      * Returns true if the selections are locked.
24089      * @return {Boolean}
24090      */
24091     isLocked : function(){
24092         return this.locked;
24093     }
24094 });
24095 /**
24096  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24097  * @class Roo.bootstrap.Table.RowSelectionModel
24098  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24099  * It supports multiple selections and keyboard selection/navigation. 
24100  * @constructor
24101  * @param {Object} config
24102  */
24103
24104 Roo.bootstrap.Table.RowSelectionModel = function(config){
24105     Roo.apply(this, config);
24106     this.selections = new Roo.util.MixedCollection(false, function(o){
24107         return o.id;
24108     });
24109
24110     this.last = false;
24111     this.lastActive = false;
24112
24113     this.addEvents({
24114         /**
24115              * @event selectionchange
24116              * Fires when the selection changes
24117              * @param {SelectionModel} this
24118              */
24119             "selectionchange" : true,
24120         /**
24121              * @event afterselectionchange
24122              * Fires after the selection changes (eg. by key press or clicking)
24123              * @param {SelectionModel} this
24124              */
24125             "afterselectionchange" : true,
24126         /**
24127              * @event beforerowselect
24128              * Fires when a row is selected being selected, return false to cancel.
24129              * @param {SelectionModel} this
24130              * @param {Number} rowIndex The selected index
24131              * @param {Boolean} keepExisting False if other selections will be cleared
24132              */
24133             "beforerowselect" : true,
24134         /**
24135              * @event rowselect
24136              * Fires when a row is selected.
24137              * @param {SelectionModel} this
24138              * @param {Number} rowIndex The selected index
24139              * @param {Roo.data.Record} r The record
24140              */
24141             "rowselect" : true,
24142         /**
24143              * @event rowdeselect
24144              * Fires when a row is deselected.
24145              * @param {SelectionModel} this
24146              * @param {Number} rowIndex The selected index
24147              */
24148         "rowdeselect" : true
24149     });
24150     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24151     this.locked = false;
24152  };
24153
24154 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24155     /**
24156      * @cfg {Boolean} singleSelect
24157      * True to allow selection of only one row at a time (defaults to false)
24158      */
24159     singleSelect : false,
24160
24161     // private
24162     initEvents : function()
24163     {
24164
24165         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24166         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24167         //}else{ // allow click to work like normal
24168          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24169         //}
24170         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24171         this.grid.on("rowclick", this.handleMouseDown, this);
24172         
24173         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24174             "up" : function(e){
24175                 if(!e.shiftKey){
24176                     this.selectPrevious(e.shiftKey);
24177                 }else if(this.last !== false && this.lastActive !== false){
24178                     var last = this.last;
24179                     this.selectRange(this.last,  this.lastActive-1);
24180                     this.grid.getView().focusRow(this.lastActive);
24181                     if(last !== false){
24182                         this.last = last;
24183                     }
24184                 }else{
24185                     this.selectFirstRow();
24186                 }
24187                 this.fireEvent("afterselectionchange", this);
24188             },
24189             "down" : function(e){
24190                 if(!e.shiftKey){
24191                     this.selectNext(e.shiftKey);
24192                 }else if(this.last !== false && this.lastActive !== false){
24193                     var last = this.last;
24194                     this.selectRange(this.last,  this.lastActive+1);
24195                     this.grid.getView().focusRow(this.lastActive);
24196                     if(last !== false){
24197                         this.last = last;
24198                     }
24199                 }else{
24200                     this.selectFirstRow();
24201                 }
24202                 this.fireEvent("afterselectionchange", this);
24203             },
24204             scope: this
24205         });
24206         this.grid.store.on('load', function(){
24207             this.selections.clear();
24208         },this);
24209         /*
24210         var view = this.grid.view;
24211         view.on("refresh", this.onRefresh, this);
24212         view.on("rowupdated", this.onRowUpdated, this);
24213         view.on("rowremoved", this.onRemove, this);
24214         */
24215     },
24216
24217     // private
24218     onRefresh : function()
24219     {
24220         var ds = this.grid.store, i, v = this.grid.view;
24221         var s = this.selections;
24222         s.each(function(r){
24223             if((i = ds.indexOfId(r.id)) != -1){
24224                 v.onRowSelect(i);
24225             }else{
24226                 s.remove(r);
24227             }
24228         });
24229     },
24230
24231     // private
24232     onRemove : function(v, index, r){
24233         this.selections.remove(r);
24234     },
24235
24236     // private
24237     onRowUpdated : function(v, index, r){
24238         if(this.isSelected(r)){
24239             v.onRowSelect(index);
24240         }
24241     },
24242
24243     /**
24244      * Select records.
24245      * @param {Array} records The records to select
24246      * @param {Boolean} keepExisting (optional) True to keep existing selections
24247      */
24248     selectRecords : function(records, keepExisting)
24249     {
24250         if(!keepExisting){
24251             this.clearSelections();
24252         }
24253             var ds = this.grid.store;
24254         for(var i = 0, len = records.length; i < len; i++){
24255             this.selectRow(ds.indexOf(records[i]), true);
24256         }
24257     },
24258
24259     /**
24260      * Gets the number of selected rows.
24261      * @return {Number}
24262      */
24263     getCount : function(){
24264         return this.selections.length;
24265     },
24266
24267     /**
24268      * Selects the first row in the grid.
24269      */
24270     selectFirstRow : function(){
24271         this.selectRow(0);
24272     },
24273
24274     /**
24275      * Select the last row.
24276      * @param {Boolean} keepExisting (optional) True to keep existing selections
24277      */
24278     selectLastRow : function(keepExisting){
24279         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24280         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24281     },
24282
24283     /**
24284      * Selects the row immediately following the last selected row.
24285      * @param {Boolean} keepExisting (optional) True to keep existing selections
24286      */
24287     selectNext : function(keepExisting)
24288     {
24289             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24290             this.selectRow(this.last+1, keepExisting);
24291             this.grid.getView().focusRow(this.last);
24292         }
24293     },
24294
24295     /**
24296      * Selects the row that precedes the last selected row.
24297      * @param {Boolean} keepExisting (optional) True to keep existing selections
24298      */
24299     selectPrevious : function(keepExisting){
24300         if(this.last){
24301             this.selectRow(this.last-1, keepExisting);
24302             this.grid.getView().focusRow(this.last);
24303         }
24304     },
24305
24306     /**
24307      * Returns the selected records
24308      * @return {Array} Array of selected records
24309      */
24310     getSelections : function(){
24311         return [].concat(this.selections.items);
24312     },
24313
24314     /**
24315      * Returns the first selected record.
24316      * @return {Record}
24317      */
24318     getSelected : function(){
24319         return this.selections.itemAt(0);
24320     },
24321
24322
24323     /**
24324      * Clears all selections.
24325      */
24326     clearSelections : function(fast)
24327     {
24328         if(this.locked) {
24329             return;
24330         }
24331         if(fast !== true){
24332                 var ds = this.grid.store;
24333             var s = this.selections;
24334             s.each(function(r){
24335                 this.deselectRow(ds.indexOfId(r.id));
24336             }, this);
24337             s.clear();
24338         }else{
24339             this.selections.clear();
24340         }
24341         this.last = false;
24342     },
24343
24344
24345     /**
24346      * Selects all rows.
24347      */
24348     selectAll : function(){
24349         if(this.locked) {
24350             return;
24351         }
24352         this.selections.clear();
24353         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24354             this.selectRow(i, true);
24355         }
24356     },
24357
24358     /**
24359      * Returns True if there is a selection.
24360      * @return {Boolean}
24361      */
24362     hasSelection : function(){
24363         return this.selections.length > 0;
24364     },
24365
24366     /**
24367      * Returns True if the specified row is selected.
24368      * @param {Number/Record} record The record or index of the record to check
24369      * @return {Boolean}
24370      */
24371     isSelected : function(index){
24372             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24373         return (r && this.selections.key(r.id) ? true : false);
24374     },
24375
24376     /**
24377      * Returns True if the specified record id is selected.
24378      * @param {String} id The id of record to check
24379      * @return {Boolean}
24380      */
24381     isIdSelected : function(id){
24382         return (this.selections.key(id) ? true : false);
24383     },
24384
24385
24386     // private
24387     handleMouseDBClick : function(e, t){
24388         
24389     },
24390     // private
24391     handleMouseDown : function(e, t)
24392     {
24393             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24394         if(this.isLocked() || rowIndex < 0 ){
24395             return;
24396         };
24397         if(e.shiftKey && this.last !== false){
24398             var last = this.last;
24399             this.selectRange(last, rowIndex, e.ctrlKey);
24400             this.last = last; // reset the last
24401             t.focus();
24402     
24403         }else{
24404             var isSelected = this.isSelected(rowIndex);
24405             //Roo.log("select row:" + rowIndex);
24406             if(isSelected){
24407                 this.deselectRow(rowIndex);
24408             } else {
24409                         this.selectRow(rowIndex, true);
24410             }
24411     
24412             /*
24413                 if(e.button !== 0 && isSelected){
24414                 alert('rowIndex 2: ' + rowIndex);
24415                     view.focusRow(rowIndex);
24416                 }else if(e.ctrlKey && isSelected){
24417                     this.deselectRow(rowIndex);
24418                 }else if(!isSelected){
24419                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24420                     view.focusRow(rowIndex);
24421                 }
24422             */
24423         }
24424         this.fireEvent("afterselectionchange", this);
24425     },
24426     // private
24427     handleDragableRowClick :  function(grid, rowIndex, e) 
24428     {
24429         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24430             this.selectRow(rowIndex, false);
24431             grid.view.focusRow(rowIndex);
24432              this.fireEvent("afterselectionchange", this);
24433         }
24434     },
24435     
24436     /**
24437      * Selects multiple rows.
24438      * @param {Array} rows Array of the indexes of the row to select
24439      * @param {Boolean} keepExisting (optional) True to keep existing selections
24440      */
24441     selectRows : function(rows, keepExisting){
24442         if(!keepExisting){
24443             this.clearSelections();
24444         }
24445         for(var i = 0, len = rows.length; i < len; i++){
24446             this.selectRow(rows[i], true);
24447         }
24448     },
24449
24450     /**
24451      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24452      * @param {Number} startRow The index of the first row in the range
24453      * @param {Number} endRow The index of the last row in the range
24454      * @param {Boolean} keepExisting (optional) True to retain existing selections
24455      */
24456     selectRange : function(startRow, endRow, keepExisting){
24457         if(this.locked) {
24458             return;
24459         }
24460         if(!keepExisting){
24461             this.clearSelections();
24462         }
24463         if(startRow <= endRow){
24464             for(var i = startRow; i <= endRow; i++){
24465                 this.selectRow(i, true);
24466             }
24467         }else{
24468             for(var i = startRow; i >= endRow; i--){
24469                 this.selectRow(i, true);
24470             }
24471         }
24472     },
24473
24474     /**
24475      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24476      * @param {Number} startRow The index of the first row in the range
24477      * @param {Number} endRow The index of the last row in the range
24478      */
24479     deselectRange : function(startRow, endRow, preventViewNotify){
24480         if(this.locked) {
24481             return;
24482         }
24483         for(var i = startRow; i <= endRow; i++){
24484             this.deselectRow(i, preventViewNotify);
24485         }
24486     },
24487
24488     /**
24489      * Selects a row.
24490      * @param {Number} row The index of the row to select
24491      * @param {Boolean} keepExisting (optional) True to keep existing selections
24492      */
24493     selectRow : function(index, keepExisting, preventViewNotify)
24494     {
24495             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24496             return;
24497         }
24498         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24499             if(!keepExisting || this.singleSelect){
24500                 this.clearSelections();
24501             }
24502             
24503             var r = this.grid.store.getAt(index);
24504             //console.log('selectRow - record id :' + r.id);
24505             
24506             this.selections.add(r);
24507             this.last = this.lastActive = index;
24508             if(!preventViewNotify){
24509                 var proxy = new Roo.Element(
24510                                 this.grid.getRowDom(index)
24511                 );
24512                 proxy.addClass('bg-info info');
24513             }
24514             this.fireEvent("rowselect", this, index, r);
24515             this.fireEvent("selectionchange", this);
24516         }
24517     },
24518
24519     /**
24520      * Deselects a row.
24521      * @param {Number} row The index of the row to deselect
24522      */
24523     deselectRow : function(index, preventViewNotify)
24524     {
24525         if(this.locked) {
24526             return;
24527         }
24528         if(this.last == index){
24529             this.last = false;
24530         }
24531         if(this.lastActive == index){
24532             this.lastActive = false;
24533         }
24534         
24535         var r = this.grid.store.getAt(index);
24536         if (!r) {
24537             return;
24538         }
24539         
24540         this.selections.remove(r);
24541         //.console.log('deselectRow - record id :' + r.id);
24542         if(!preventViewNotify){
24543         
24544             var proxy = new Roo.Element(
24545                 this.grid.getRowDom(index)
24546             );
24547             proxy.removeClass('bg-info info');
24548         }
24549         this.fireEvent("rowdeselect", this, index);
24550         this.fireEvent("selectionchange", this);
24551     },
24552
24553     // private
24554     restoreLast : function(){
24555         if(this._last){
24556             this.last = this._last;
24557         }
24558     },
24559
24560     // private
24561     acceptsNav : function(row, col, cm){
24562         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24563     },
24564
24565     // private
24566     onEditorKey : function(field, e){
24567         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24568         if(k == e.TAB){
24569             e.stopEvent();
24570             ed.completeEdit();
24571             if(e.shiftKey){
24572                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24573             }else{
24574                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24575             }
24576         }else if(k == e.ENTER && !e.ctrlKey){
24577             e.stopEvent();
24578             ed.completeEdit();
24579             if(e.shiftKey){
24580                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24581             }else{
24582                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24583             }
24584         }else if(k == e.ESC){
24585             ed.cancelEdit();
24586         }
24587         if(newCell){
24588             g.startEditing(newCell[0], newCell[1]);
24589         }
24590     }
24591 });
24592 /*
24593  * Based on:
24594  * Ext JS Library 1.1.1
24595  * Copyright(c) 2006-2007, Ext JS, LLC.
24596  *
24597  * Originally Released Under LGPL - original licence link has changed is not relivant.
24598  *
24599  * Fork - LGPL
24600  * <script type="text/javascript">
24601  */
24602  
24603 /**
24604  * @class Roo.bootstrap.PagingToolbar
24605  * @extends Roo.bootstrap.NavSimplebar
24606  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24607  * @constructor
24608  * Create a new PagingToolbar
24609  * @param {Object} config The config object
24610  * @param {Roo.data.Store} store
24611  */
24612 Roo.bootstrap.PagingToolbar = function(config)
24613 {
24614     // old args format still supported... - xtype is prefered..
24615         // created from xtype...
24616     
24617     this.ds = config.dataSource;
24618     
24619     if (config.store && !this.ds) {
24620         this.store= Roo.factory(config.store, Roo.data);
24621         this.ds = this.store;
24622         this.ds.xmodule = this.xmodule || false;
24623     }
24624     
24625     this.toolbarItems = [];
24626     if (config.items) {
24627         this.toolbarItems = config.items;
24628     }
24629     
24630     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24631     
24632     this.cursor = 0;
24633     
24634     if (this.ds) { 
24635         this.bind(this.ds);
24636     }
24637     
24638     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24639     
24640 };
24641
24642 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24643     /**
24644      * @cfg {Roo.data.Store} dataSource
24645      * The underlying data store providing the paged data
24646      */
24647     /**
24648      * @cfg {String/HTMLElement/Element} container
24649      * container The id or element that will contain the toolbar
24650      */
24651     /**
24652      * @cfg {Boolean} displayInfo
24653      * True to display the displayMsg (defaults to false)
24654      */
24655     /**
24656      * @cfg {Number} pageSize
24657      * The number of records to display per page (defaults to 20)
24658      */
24659     pageSize: 20,
24660     /**
24661      * @cfg {String} displayMsg
24662      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24663      */
24664     displayMsg : 'Displaying {0} - {1} of {2}',
24665     /**
24666      * @cfg {String} emptyMsg
24667      * The message to display when no records are found (defaults to "No data to display")
24668      */
24669     emptyMsg : 'No data to display',
24670     /**
24671      * Customizable piece of the default paging text (defaults to "Page")
24672      * @type String
24673      */
24674     beforePageText : "Page",
24675     /**
24676      * Customizable piece of the default paging text (defaults to "of %0")
24677      * @type String
24678      */
24679     afterPageText : "of {0}",
24680     /**
24681      * Customizable piece of the default paging text (defaults to "First Page")
24682      * @type String
24683      */
24684     firstText : "First Page",
24685     /**
24686      * Customizable piece of the default paging text (defaults to "Previous Page")
24687      * @type String
24688      */
24689     prevText : "Previous Page",
24690     /**
24691      * Customizable piece of the default paging text (defaults to "Next Page")
24692      * @type String
24693      */
24694     nextText : "Next Page",
24695     /**
24696      * Customizable piece of the default paging text (defaults to "Last Page")
24697      * @type String
24698      */
24699     lastText : "Last Page",
24700     /**
24701      * Customizable piece of the default paging text (defaults to "Refresh")
24702      * @type String
24703      */
24704     refreshText : "Refresh",
24705
24706     buttons : false,
24707     // private
24708     onRender : function(ct, position) 
24709     {
24710         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24711         this.navgroup.parentId = this.id;
24712         this.navgroup.onRender(this.el, null);
24713         // add the buttons to the navgroup
24714         
24715         if(this.displayInfo){
24716             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24717             this.displayEl = this.el.select('.x-paging-info', true).first();
24718 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24719 //            this.displayEl = navel.el.select('span',true).first();
24720         }
24721         
24722         var _this = this;
24723         
24724         if(this.buttons){
24725             Roo.each(_this.buttons, function(e){ // this might need to use render????
24726                Roo.factory(e).render(_this.el);
24727             });
24728         }
24729             
24730         Roo.each(_this.toolbarItems, function(e) {
24731             _this.navgroup.addItem(e);
24732         });
24733         
24734         
24735         this.first = this.navgroup.addItem({
24736             tooltip: this.firstText,
24737             cls: "prev",
24738             icon : 'fa fa-step-backward',
24739             disabled: true,
24740             preventDefault: true,
24741             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24742         });
24743         
24744         this.prev =  this.navgroup.addItem({
24745             tooltip: this.prevText,
24746             cls: "prev",
24747             icon : 'fa fa-backward',
24748             disabled: true,
24749             preventDefault: true,
24750             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24751         });
24752     //this.addSeparator();
24753         
24754         
24755         var field = this.navgroup.addItem( {
24756             tagtype : 'span',
24757             cls : 'x-paging-position',
24758             
24759             html : this.beforePageText  +
24760                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24761                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24762          } ); //?? escaped?
24763         
24764         this.field = field.el.select('input', true).first();
24765         this.field.on("keydown", this.onPagingKeydown, this);
24766         this.field.on("focus", function(){this.dom.select();});
24767     
24768     
24769         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24770         //this.field.setHeight(18);
24771         //this.addSeparator();
24772         this.next = this.navgroup.addItem({
24773             tooltip: this.nextText,
24774             cls: "next",
24775             html : ' <i class="fa fa-forward">',
24776             disabled: true,
24777             preventDefault: true,
24778             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24779         });
24780         this.last = this.navgroup.addItem({
24781             tooltip: this.lastText,
24782             icon : 'fa fa-step-forward',
24783             cls: "next",
24784             disabled: true,
24785             preventDefault: true,
24786             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24787         });
24788     //this.addSeparator();
24789         this.loading = this.navgroup.addItem({
24790             tooltip: this.refreshText,
24791             icon: 'fa fa-refresh',
24792             preventDefault: true,
24793             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24794         });
24795         
24796     },
24797
24798     // private
24799     updateInfo : function(){
24800         if(this.displayEl){
24801             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24802             var msg = count == 0 ?
24803                 this.emptyMsg :
24804                 String.format(
24805                     this.displayMsg,
24806                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24807                 );
24808             this.displayEl.update(msg);
24809         }
24810     },
24811
24812     // private
24813     onLoad : function(ds, r, o)
24814     {
24815         this.cursor = o.params.start ? o.params.start : 0;
24816         
24817         var d = this.getPageData(),
24818             ap = d.activePage,
24819             ps = d.pages;
24820         
24821         
24822         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24823         this.field.dom.value = ap;
24824         this.first.setDisabled(ap == 1);
24825         this.prev.setDisabled(ap == 1);
24826         this.next.setDisabled(ap == ps);
24827         this.last.setDisabled(ap == ps);
24828         this.loading.enable();
24829         this.updateInfo();
24830     },
24831
24832     // private
24833     getPageData : function(){
24834         var total = this.ds.getTotalCount();
24835         return {
24836             total : total,
24837             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24838             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24839         };
24840     },
24841
24842     // private
24843     onLoadError : function(){
24844         this.loading.enable();
24845     },
24846
24847     // private
24848     onPagingKeydown : function(e){
24849         var k = e.getKey();
24850         var d = this.getPageData();
24851         if(k == e.RETURN){
24852             var v = this.field.dom.value, pageNum;
24853             if(!v || isNaN(pageNum = parseInt(v, 10))){
24854                 this.field.dom.value = d.activePage;
24855                 return;
24856             }
24857             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24858             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24859             e.stopEvent();
24860         }
24861         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))
24862         {
24863           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24864           this.field.dom.value = pageNum;
24865           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24866           e.stopEvent();
24867         }
24868         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24869         {
24870           var v = this.field.dom.value, pageNum; 
24871           var increment = (e.shiftKey) ? 10 : 1;
24872           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24873                 increment *= -1;
24874           }
24875           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24876             this.field.dom.value = d.activePage;
24877             return;
24878           }
24879           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24880           {
24881             this.field.dom.value = parseInt(v, 10) + increment;
24882             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24883             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24884           }
24885           e.stopEvent();
24886         }
24887     },
24888
24889     // private
24890     beforeLoad : function(){
24891         if(this.loading){
24892             this.loading.disable();
24893         }
24894     },
24895
24896     // private
24897     onClick : function(which){
24898         
24899         var ds = this.ds;
24900         if (!ds) {
24901             return;
24902         }
24903         
24904         switch(which){
24905             case "first":
24906                 ds.load({params:{start: 0, limit: this.pageSize}});
24907             break;
24908             case "prev":
24909                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24910             break;
24911             case "next":
24912                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24913             break;
24914             case "last":
24915                 var total = ds.getTotalCount();
24916                 var extra = total % this.pageSize;
24917                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24918                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24919             break;
24920             case "refresh":
24921                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24922             break;
24923         }
24924     },
24925
24926     /**
24927      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24928      * @param {Roo.data.Store} store The data store to unbind
24929      */
24930     unbind : function(ds){
24931         ds.un("beforeload", this.beforeLoad, this);
24932         ds.un("load", this.onLoad, this);
24933         ds.un("loadexception", this.onLoadError, this);
24934         ds.un("remove", this.updateInfo, this);
24935         ds.un("add", this.updateInfo, this);
24936         this.ds = undefined;
24937     },
24938
24939     /**
24940      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24941      * @param {Roo.data.Store} store The data store to bind
24942      */
24943     bind : function(ds){
24944         ds.on("beforeload", this.beforeLoad, this);
24945         ds.on("load", this.onLoad, this);
24946         ds.on("loadexception", this.onLoadError, this);
24947         ds.on("remove", this.updateInfo, this);
24948         ds.on("add", this.updateInfo, this);
24949         this.ds = ds;
24950     }
24951 });/*
24952  * - LGPL
24953  *
24954  * element
24955  * 
24956  */
24957
24958 /**
24959  * @class Roo.bootstrap.MessageBar
24960  * @extends Roo.bootstrap.Component
24961  * Bootstrap MessageBar class
24962  * @cfg {String} html contents of the MessageBar
24963  * @cfg {String} weight (info | success | warning | danger) default info
24964  * @cfg {String} beforeClass insert the bar before the given class
24965  * @cfg {Boolean} closable (true | false) default false
24966  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24967  * 
24968  * @constructor
24969  * Create a new Element
24970  * @param {Object} config The config object
24971  */
24972
24973 Roo.bootstrap.MessageBar = function(config){
24974     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24975 };
24976
24977 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24978     
24979     html: '',
24980     weight: 'info',
24981     closable: false,
24982     fixed: false,
24983     beforeClass: 'bootstrap-sticky-wrap',
24984     
24985     getAutoCreate : function(){
24986         
24987         var cfg = {
24988             tag: 'div',
24989             cls: 'alert alert-dismissable alert-' + this.weight,
24990             cn: [
24991                 {
24992                     tag: 'span',
24993                     cls: 'message',
24994                     html: this.html || ''
24995                 }
24996             ]
24997         };
24998         
24999         if(this.fixed){
25000             cfg.cls += ' alert-messages-fixed';
25001         }
25002         
25003         if(this.closable){
25004             cfg.cn.push({
25005                 tag: 'button',
25006                 cls: 'close',
25007                 html: 'x'
25008             });
25009         }
25010         
25011         return cfg;
25012     },
25013     
25014     onRender : function(ct, position)
25015     {
25016         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25017         
25018         if(!this.el){
25019             var cfg = Roo.apply({},  this.getAutoCreate());
25020             cfg.id = Roo.id();
25021             
25022             if (this.cls) {
25023                 cfg.cls += ' ' + this.cls;
25024             }
25025             if (this.style) {
25026                 cfg.style = this.style;
25027             }
25028             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25029             
25030             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25031         }
25032         
25033         this.el.select('>button.close').on('click', this.hide, this);
25034         
25035     },
25036     
25037     show : function()
25038     {
25039         if (!this.rendered) {
25040             this.render();
25041         }
25042         
25043         this.el.show();
25044         
25045         this.fireEvent('show', this);
25046         
25047     },
25048     
25049     hide : function()
25050     {
25051         if (!this.rendered) {
25052             this.render();
25053         }
25054         
25055         this.el.hide();
25056         
25057         this.fireEvent('hide', this);
25058     },
25059     
25060     update : function()
25061     {
25062 //        var e = this.el.dom.firstChild;
25063 //        
25064 //        if(this.closable){
25065 //            e = e.nextSibling;
25066 //        }
25067 //        
25068 //        e.data = this.html || '';
25069
25070         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25071     }
25072    
25073 });
25074
25075  
25076
25077      /*
25078  * - LGPL
25079  *
25080  * Graph
25081  * 
25082  */
25083
25084
25085 /**
25086  * @class Roo.bootstrap.Graph
25087  * @extends Roo.bootstrap.Component
25088  * Bootstrap Graph class
25089 > Prameters
25090  -sm {number} sm 4
25091  -md {number} md 5
25092  @cfg {String} graphtype  bar | vbar | pie
25093  @cfg {number} g_x coodinator | centre x (pie)
25094  @cfg {number} g_y coodinator | centre y (pie)
25095  @cfg {number} g_r radius (pie)
25096  @cfg {number} g_height height of the chart (respected by all elements in the set)
25097  @cfg {number} g_width width of the chart (respected by all elements in the set)
25098  @cfg {Object} title The title of the chart
25099     
25100  -{Array}  values
25101  -opts (object) options for the chart 
25102      o {
25103      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25104      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25105      o vgutter (number)
25106      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.
25107      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25108      o to
25109      o stretch (boolean)
25110      o }
25111  -opts (object) options for the pie
25112      o{
25113      o cut
25114      o startAngle (number)
25115      o endAngle (number)
25116      } 
25117  *
25118  * @constructor
25119  * Create a new Input
25120  * @param {Object} config The config object
25121  */
25122
25123 Roo.bootstrap.Graph = function(config){
25124     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25125     
25126     this.addEvents({
25127         // img events
25128         /**
25129          * @event click
25130          * The img click event for the img.
25131          * @param {Roo.EventObject} e
25132          */
25133         "click" : true
25134     });
25135 };
25136
25137 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25138     
25139     sm: 4,
25140     md: 5,
25141     graphtype: 'bar',
25142     g_height: 250,
25143     g_width: 400,
25144     g_x: 50,
25145     g_y: 50,
25146     g_r: 30,
25147     opts:{
25148         //g_colors: this.colors,
25149         g_type: 'soft',
25150         g_gutter: '20%'
25151
25152     },
25153     title : false,
25154
25155     getAutoCreate : function(){
25156         
25157         var cfg = {
25158             tag: 'div',
25159             html : null
25160         };
25161         
25162         
25163         return  cfg;
25164     },
25165
25166     onRender : function(ct,position){
25167         
25168         
25169         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25170         
25171         if (typeof(Raphael) == 'undefined') {
25172             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25173             return;
25174         }
25175         
25176         this.raphael = Raphael(this.el.dom);
25177         
25178                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25179                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25180                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25181                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25182                 /*
25183                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25184                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25185                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25186                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25187                 
25188                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25189                 r.barchart(330, 10, 300, 220, data1);
25190                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25191                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25192                 */
25193                 
25194                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25195                 // r.barchart(30, 30, 560, 250,  xdata, {
25196                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25197                 //     axis : "0 0 1 1",
25198                 //     axisxlabels :  xdata
25199                 //     //yvalues : cols,
25200                    
25201                 // });
25202 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25203 //        
25204 //        this.load(null,xdata,{
25205 //                axis : "0 0 1 1",
25206 //                axisxlabels :  xdata
25207 //                });
25208
25209     },
25210
25211     load : function(graphtype,xdata,opts)
25212     {
25213         this.raphael.clear();
25214         if(!graphtype) {
25215             graphtype = this.graphtype;
25216         }
25217         if(!opts){
25218             opts = this.opts;
25219         }
25220         var r = this.raphael,
25221             fin = function () {
25222                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25223             },
25224             fout = function () {
25225                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25226             },
25227             pfin = function() {
25228                 this.sector.stop();
25229                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25230
25231                 if (this.label) {
25232                     this.label[0].stop();
25233                     this.label[0].attr({ r: 7.5 });
25234                     this.label[1].attr({ "font-weight": 800 });
25235                 }
25236             },
25237             pfout = function() {
25238                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25239
25240                 if (this.label) {
25241                     this.label[0].animate({ r: 5 }, 500, "bounce");
25242                     this.label[1].attr({ "font-weight": 400 });
25243                 }
25244             };
25245
25246         switch(graphtype){
25247             case 'bar':
25248                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25249                 break;
25250             case 'hbar':
25251                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25252                 break;
25253             case 'pie':
25254 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25255 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25256 //            
25257                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25258                 
25259                 break;
25260
25261         }
25262         
25263         if(this.title){
25264             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25265         }
25266         
25267     },
25268     
25269     setTitle: function(o)
25270     {
25271         this.title = o;
25272     },
25273     
25274     initEvents: function() {
25275         
25276         if(!this.href){
25277             this.el.on('click', this.onClick, this);
25278         }
25279     },
25280     
25281     onClick : function(e)
25282     {
25283         Roo.log('img onclick');
25284         this.fireEvent('click', this, e);
25285     }
25286    
25287 });
25288
25289  
25290 /*
25291  * - LGPL
25292  *
25293  * numberBox
25294  * 
25295  */
25296 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25297
25298 /**
25299  * @class Roo.bootstrap.dash.NumberBox
25300  * @extends Roo.bootstrap.Component
25301  * Bootstrap NumberBox class
25302  * @cfg {String} headline Box headline
25303  * @cfg {String} content Box content
25304  * @cfg {String} icon Box icon
25305  * @cfg {String} footer Footer text
25306  * @cfg {String} fhref Footer href
25307  * 
25308  * @constructor
25309  * Create a new NumberBox
25310  * @param {Object} config The config object
25311  */
25312
25313
25314 Roo.bootstrap.dash.NumberBox = function(config){
25315     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25316     
25317 };
25318
25319 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25320     
25321     headline : '',
25322     content : '',
25323     icon : '',
25324     footer : '',
25325     fhref : '',
25326     ficon : '',
25327     
25328     getAutoCreate : function(){
25329         
25330         var cfg = {
25331             tag : 'div',
25332             cls : 'small-box ',
25333             cn : [
25334                 {
25335                     tag : 'div',
25336                     cls : 'inner',
25337                     cn :[
25338                         {
25339                             tag : 'h3',
25340                             cls : 'roo-headline',
25341                             html : this.headline
25342                         },
25343                         {
25344                             tag : 'p',
25345                             cls : 'roo-content',
25346                             html : this.content
25347                         }
25348                     ]
25349                 }
25350             ]
25351         };
25352         
25353         if(this.icon){
25354             cfg.cn.push({
25355                 tag : 'div',
25356                 cls : 'icon',
25357                 cn :[
25358                     {
25359                         tag : 'i',
25360                         cls : 'ion ' + this.icon
25361                     }
25362                 ]
25363             });
25364         }
25365         
25366         if(this.footer){
25367             var footer = {
25368                 tag : 'a',
25369                 cls : 'small-box-footer',
25370                 href : this.fhref || '#',
25371                 html : this.footer
25372             };
25373             
25374             cfg.cn.push(footer);
25375             
25376         }
25377         
25378         return  cfg;
25379     },
25380
25381     onRender : function(ct,position){
25382         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25383
25384
25385        
25386                 
25387     },
25388
25389     setHeadline: function (value)
25390     {
25391         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25392     },
25393     
25394     setFooter: function (value, href)
25395     {
25396         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25397         
25398         if(href){
25399             this.el.select('a.small-box-footer',true).first().attr('href', href);
25400         }
25401         
25402     },
25403
25404     setContent: function (value)
25405     {
25406         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25407     },
25408
25409     initEvents: function() 
25410     {   
25411         
25412     }
25413     
25414 });
25415
25416  
25417 /*
25418  * - LGPL
25419  *
25420  * TabBox
25421  * 
25422  */
25423 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25424
25425 /**
25426  * @class Roo.bootstrap.dash.TabBox
25427  * @extends Roo.bootstrap.Component
25428  * Bootstrap TabBox class
25429  * @cfg {String} title Title of the TabBox
25430  * @cfg {String} icon Icon of the TabBox
25431  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25432  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25433  * 
25434  * @constructor
25435  * Create a new TabBox
25436  * @param {Object} config The config object
25437  */
25438
25439
25440 Roo.bootstrap.dash.TabBox = function(config){
25441     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25442     this.addEvents({
25443         // raw events
25444         /**
25445          * @event addpane
25446          * When a pane is added
25447          * @param {Roo.bootstrap.dash.TabPane} pane
25448          */
25449         "addpane" : true,
25450         /**
25451          * @event activatepane
25452          * When a pane is activated
25453          * @param {Roo.bootstrap.dash.TabPane} pane
25454          */
25455         "activatepane" : true
25456         
25457          
25458     });
25459     
25460     this.panes = [];
25461 };
25462
25463 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25464
25465     title : '',
25466     icon : false,
25467     showtabs : true,
25468     tabScrollable : false,
25469     
25470     getChildContainer : function()
25471     {
25472         return this.el.select('.tab-content', true).first();
25473     },
25474     
25475     getAutoCreate : function(){
25476         
25477         var header = {
25478             tag: 'li',
25479             cls: 'pull-left header',
25480             html: this.title,
25481             cn : []
25482         };
25483         
25484         if(this.icon){
25485             header.cn.push({
25486                 tag: 'i',
25487                 cls: 'fa ' + this.icon
25488             });
25489         }
25490         
25491         var h = {
25492             tag: 'ul',
25493             cls: 'nav nav-tabs pull-right',
25494             cn: [
25495                 header
25496             ]
25497         };
25498         
25499         if(this.tabScrollable){
25500             h = {
25501                 tag: 'div',
25502                 cls: 'tab-header',
25503                 cn: [
25504                     {
25505                         tag: 'ul',
25506                         cls: 'nav nav-tabs pull-right',
25507                         cn: [
25508                             header
25509                         ]
25510                     }
25511                 ]
25512             };
25513         }
25514         
25515         var cfg = {
25516             tag: 'div',
25517             cls: 'nav-tabs-custom',
25518             cn: [
25519                 h,
25520                 {
25521                     tag: 'div',
25522                     cls: 'tab-content no-padding',
25523                     cn: []
25524                 }
25525             ]
25526         };
25527
25528         return  cfg;
25529     },
25530     initEvents : function()
25531     {
25532         //Roo.log('add add pane handler');
25533         this.on('addpane', this.onAddPane, this);
25534     },
25535      /**
25536      * Updates the box title
25537      * @param {String} html to set the title to.
25538      */
25539     setTitle : function(value)
25540     {
25541         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25542     },
25543     onAddPane : function(pane)
25544     {
25545         this.panes.push(pane);
25546         //Roo.log('addpane');
25547         //Roo.log(pane);
25548         // tabs are rendere left to right..
25549         if(!this.showtabs){
25550             return;
25551         }
25552         
25553         var ctr = this.el.select('.nav-tabs', true).first();
25554          
25555          
25556         var existing = ctr.select('.nav-tab',true);
25557         var qty = existing.getCount();;
25558         
25559         
25560         var tab = ctr.createChild({
25561             tag : 'li',
25562             cls : 'nav-tab' + (qty ? '' : ' active'),
25563             cn : [
25564                 {
25565                     tag : 'a',
25566                     href:'#',
25567                     html : pane.title
25568                 }
25569             ]
25570         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25571         pane.tab = tab;
25572         
25573         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25574         if (!qty) {
25575             pane.el.addClass('active');
25576         }
25577         
25578                 
25579     },
25580     onTabClick : function(ev,un,ob,pane)
25581     {
25582         //Roo.log('tab - prev default');
25583         ev.preventDefault();
25584         
25585         
25586         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25587         pane.tab.addClass('active');
25588         //Roo.log(pane.title);
25589         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25590         // technically we should have a deactivate event.. but maybe add later.
25591         // and it should not de-activate the selected tab...
25592         this.fireEvent('activatepane', pane);
25593         pane.el.addClass('active');
25594         pane.fireEvent('activate');
25595         
25596         
25597     },
25598     
25599     getActivePane : function()
25600     {
25601         var r = false;
25602         Roo.each(this.panes, function(p) {
25603             if(p.el.hasClass('active')){
25604                 r = p;
25605                 return false;
25606             }
25607             
25608             return;
25609         });
25610         
25611         return r;
25612     }
25613     
25614     
25615 });
25616
25617  
25618 /*
25619  * - LGPL
25620  *
25621  * Tab pane
25622  * 
25623  */
25624 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25625 /**
25626  * @class Roo.bootstrap.TabPane
25627  * @extends Roo.bootstrap.Component
25628  * Bootstrap TabPane class
25629  * @cfg {Boolean} active (false | true) Default false
25630  * @cfg {String} title title of panel
25631
25632  * 
25633  * @constructor
25634  * Create a new TabPane
25635  * @param {Object} config The config object
25636  */
25637
25638 Roo.bootstrap.dash.TabPane = function(config){
25639     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25640     
25641     this.addEvents({
25642         // raw events
25643         /**
25644          * @event activate
25645          * When a pane is activated
25646          * @param {Roo.bootstrap.dash.TabPane} pane
25647          */
25648         "activate" : true
25649          
25650     });
25651 };
25652
25653 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25654     
25655     active : false,
25656     title : '',
25657     
25658     // the tabBox that this is attached to.
25659     tab : false,
25660      
25661     getAutoCreate : function() 
25662     {
25663         var cfg = {
25664             tag: 'div',
25665             cls: 'tab-pane'
25666         };
25667         
25668         if(this.active){
25669             cfg.cls += ' active';
25670         }
25671         
25672         return cfg;
25673     },
25674     initEvents  : function()
25675     {
25676         //Roo.log('trigger add pane handler');
25677         this.parent().fireEvent('addpane', this)
25678     },
25679     
25680      /**
25681      * Updates the tab title 
25682      * @param {String} html to set the title to.
25683      */
25684     setTitle: function(str)
25685     {
25686         if (!this.tab) {
25687             return;
25688         }
25689         this.title = str;
25690         this.tab.select('a', true).first().dom.innerHTML = str;
25691         
25692     }
25693     
25694     
25695     
25696 });
25697
25698  
25699
25700
25701  /*
25702  * - LGPL
25703  *
25704  * menu
25705  * 
25706  */
25707 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25708
25709 /**
25710  * @class Roo.bootstrap.menu.Menu
25711  * @extends Roo.bootstrap.Component
25712  * Bootstrap Menu class - container for Menu
25713  * @cfg {String} html Text of the menu
25714  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25715  * @cfg {String} icon Font awesome icon
25716  * @cfg {String} pos Menu align to (top | bottom) default bottom
25717  * 
25718  * 
25719  * @constructor
25720  * Create a new Menu
25721  * @param {Object} config The config object
25722  */
25723
25724
25725 Roo.bootstrap.menu.Menu = function(config){
25726     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25727     
25728     this.addEvents({
25729         /**
25730          * @event beforeshow
25731          * Fires before this menu is displayed
25732          * @param {Roo.bootstrap.menu.Menu} this
25733          */
25734         beforeshow : true,
25735         /**
25736          * @event beforehide
25737          * Fires before this menu is hidden
25738          * @param {Roo.bootstrap.menu.Menu} this
25739          */
25740         beforehide : true,
25741         /**
25742          * @event show
25743          * Fires after this menu is displayed
25744          * @param {Roo.bootstrap.menu.Menu} this
25745          */
25746         show : true,
25747         /**
25748          * @event hide
25749          * Fires after this menu is hidden
25750          * @param {Roo.bootstrap.menu.Menu} this
25751          */
25752         hide : true,
25753         /**
25754          * @event click
25755          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25756          * @param {Roo.bootstrap.menu.Menu} this
25757          * @param {Roo.EventObject} e
25758          */
25759         click : true
25760     });
25761     
25762 };
25763
25764 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25765     
25766     submenu : false,
25767     html : '',
25768     weight : 'default',
25769     icon : false,
25770     pos : 'bottom',
25771     
25772     
25773     getChildContainer : function() {
25774         if(this.isSubMenu){
25775             return this.el;
25776         }
25777         
25778         return this.el.select('ul.dropdown-menu', true).first();  
25779     },
25780     
25781     getAutoCreate : function()
25782     {
25783         var text = [
25784             {
25785                 tag : 'span',
25786                 cls : 'roo-menu-text',
25787                 html : this.html
25788             }
25789         ];
25790         
25791         if(this.icon){
25792             text.unshift({
25793                 tag : 'i',
25794                 cls : 'fa ' + this.icon
25795             })
25796         }
25797         
25798         
25799         var cfg = {
25800             tag : 'div',
25801             cls : 'btn-group',
25802             cn : [
25803                 {
25804                     tag : 'button',
25805                     cls : 'dropdown-button btn btn-' + this.weight,
25806                     cn : text
25807                 },
25808                 {
25809                     tag : 'button',
25810                     cls : 'dropdown-toggle btn btn-' + this.weight,
25811                     cn : [
25812                         {
25813                             tag : 'span',
25814                             cls : 'caret'
25815                         }
25816                     ]
25817                 },
25818                 {
25819                     tag : 'ul',
25820                     cls : 'dropdown-menu'
25821                 }
25822             ]
25823             
25824         };
25825         
25826         if(this.pos == 'top'){
25827             cfg.cls += ' dropup';
25828         }
25829         
25830         if(this.isSubMenu){
25831             cfg = {
25832                 tag : 'ul',
25833                 cls : 'dropdown-menu'
25834             }
25835         }
25836         
25837         return cfg;
25838     },
25839     
25840     onRender : function(ct, position)
25841     {
25842         this.isSubMenu = ct.hasClass('dropdown-submenu');
25843         
25844         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25845     },
25846     
25847     initEvents : function() 
25848     {
25849         if(this.isSubMenu){
25850             return;
25851         }
25852         
25853         this.hidden = true;
25854         
25855         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25856         this.triggerEl.on('click', this.onTriggerPress, this);
25857         
25858         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25859         this.buttonEl.on('click', this.onClick, this);
25860         
25861     },
25862     
25863     list : function()
25864     {
25865         if(this.isSubMenu){
25866             return this.el;
25867         }
25868         
25869         return this.el.select('ul.dropdown-menu', true).first();
25870     },
25871     
25872     onClick : function(e)
25873     {
25874         this.fireEvent("click", this, e);
25875     },
25876     
25877     onTriggerPress  : function(e)
25878     {   
25879         if (this.isVisible()) {
25880             this.hide();
25881         } else {
25882             this.show();
25883         }
25884     },
25885     
25886     isVisible : function(){
25887         return !this.hidden;
25888     },
25889     
25890     show : function()
25891     {
25892         this.fireEvent("beforeshow", this);
25893         
25894         this.hidden = false;
25895         this.el.addClass('open');
25896         
25897         Roo.get(document).on("mouseup", this.onMouseUp, this);
25898         
25899         this.fireEvent("show", this);
25900         
25901         
25902     },
25903     
25904     hide : function()
25905     {
25906         this.fireEvent("beforehide", this);
25907         
25908         this.hidden = true;
25909         this.el.removeClass('open');
25910         
25911         Roo.get(document).un("mouseup", this.onMouseUp);
25912         
25913         this.fireEvent("hide", this);
25914     },
25915     
25916     onMouseUp : function()
25917     {
25918         this.hide();
25919     }
25920     
25921 });
25922
25923  
25924  /*
25925  * - LGPL
25926  *
25927  * menu item
25928  * 
25929  */
25930 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25931
25932 /**
25933  * @class Roo.bootstrap.menu.Item
25934  * @extends Roo.bootstrap.Component
25935  * Bootstrap MenuItem class
25936  * @cfg {Boolean} submenu (true | false) default false
25937  * @cfg {String} html text of the item
25938  * @cfg {String} href the link
25939  * @cfg {Boolean} disable (true | false) default false
25940  * @cfg {Boolean} preventDefault (true | false) default true
25941  * @cfg {String} icon Font awesome icon
25942  * @cfg {String} pos Submenu align to (left | right) default right 
25943  * 
25944  * 
25945  * @constructor
25946  * Create a new Item
25947  * @param {Object} config The config object
25948  */
25949
25950
25951 Roo.bootstrap.menu.Item = function(config){
25952     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25953     this.addEvents({
25954         /**
25955          * @event mouseover
25956          * Fires when the mouse is hovering over this menu
25957          * @param {Roo.bootstrap.menu.Item} this
25958          * @param {Roo.EventObject} e
25959          */
25960         mouseover : true,
25961         /**
25962          * @event mouseout
25963          * Fires when the mouse exits this menu
25964          * @param {Roo.bootstrap.menu.Item} this
25965          * @param {Roo.EventObject} e
25966          */
25967         mouseout : true,
25968         // raw events
25969         /**
25970          * @event click
25971          * The raw click event for the entire grid.
25972          * @param {Roo.EventObject} e
25973          */
25974         click : true
25975     });
25976 };
25977
25978 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25979     
25980     submenu : false,
25981     href : '',
25982     html : '',
25983     preventDefault: true,
25984     disable : false,
25985     icon : false,
25986     pos : 'right',
25987     
25988     getAutoCreate : function()
25989     {
25990         var text = [
25991             {
25992                 tag : 'span',
25993                 cls : 'roo-menu-item-text',
25994                 html : this.html
25995             }
25996         ];
25997         
25998         if(this.icon){
25999             text.unshift({
26000                 tag : 'i',
26001                 cls : 'fa ' + this.icon
26002             })
26003         }
26004         
26005         var cfg = {
26006             tag : 'li',
26007             cn : [
26008                 {
26009                     tag : 'a',
26010                     href : this.href || '#',
26011                     cn : text
26012                 }
26013             ]
26014         };
26015         
26016         if(this.disable){
26017             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26018         }
26019         
26020         if(this.submenu){
26021             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26022             
26023             if(this.pos == 'left'){
26024                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26025             }
26026         }
26027         
26028         return cfg;
26029     },
26030     
26031     initEvents : function() 
26032     {
26033         this.el.on('mouseover', this.onMouseOver, this);
26034         this.el.on('mouseout', this.onMouseOut, this);
26035         
26036         this.el.select('a', true).first().on('click', this.onClick, this);
26037         
26038     },
26039     
26040     onClick : function(e)
26041     {
26042         if(this.preventDefault){
26043             e.preventDefault();
26044         }
26045         
26046         this.fireEvent("click", this, e);
26047     },
26048     
26049     onMouseOver : function(e)
26050     {
26051         if(this.submenu && this.pos == 'left'){
26052             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26053         }
26054         
26055         this.fireEvent("mouseover", this, e);
26056     },
26057     
26058     onMouseOut : function(e)
26059     {
26060         this.fireEvent("mouseout", this, e);
26061     }
26062 });
26063
26064  
26065
26066  /*
26067  * - LGPL
26068  *
26069  * menu separator
26070  * 
26071  */
26072 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26073
26074 /**
26075  * @class Roo.bootstrap.menu.Separator
26076  * @extends Roo.bootstrap.Component
26077  * Bootstrap Separator class
26078  * 
26079  * @constructor
26080  * Create a new Separator
26081  * @param {Object} config The config object
26082  */
26083
26084
26085 Roo.bootstrap.menu.Separator = function(config){
26086     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26087 };
26088
26089 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26090     
26091     getAutoCreate : function(){
26092         var cfg = {
26093             tag : 'li',
26094             cls: 'divider'
26095         };
26096         
26097         return cfg;
26098     }
26099    
26100 });
26101
26102  
26103
26104  /*
26105  * - LGPL
26106  *
26107  * Tooltip
26108  * 
26109  */
26110
26111 /**
26112  * @class Roo.bootstrap.Tooltip
26113  * Bootstrap Tooltip class
26114  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26115  * to determine which dom element triggers the tooltip.
26116  * 
26117  * It needs to add support for additional attributes like tooltip-position
26118  * 
26119  * @constructor
26120  * Create a new Toolti
26121  * @param {Object} config The config object
26122  */
26123
26124 Roo.bootstrap.Tooltip = function(config){
26125     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26126     
26127     this.alignment = Roo.bootstrap.Tooltip.alignment;
26128     
26129     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26130         this.alignment = config.alignment;
26131     }
26132     
26133 };
26134
26135 Roo.apply(Roo.bootstrap.Tooltip, {
26136     /**
26137      * @function init initialize tooltip monitoring.
26138      * @static
26139      */
26140     currentEl : false,
26141     currentTip : false,
26142     currentRegion : false,
26143     
26144     //  init : delay?
26145     
26146     init : function()
26147     {
26148         Roo.get(document).on('mouseover', this.enter ,this);
26149         Roo.get(document).on('mouseout', this.leave, this);
26150          
26151         
26152         this.currentTip = new Roo.bootstrap.Tooltip();
26153     },
26154     
26155     enter : function(ev)
26156     {
26157         var dom = ev.getTarget();
26158         
26159         //Roo.log(['enter',dom]);
26160         var el = Roo.fly(dom);
26161         if (this.currentEl) {
26162             //Roo.log(dom);
26163             //Roo.log(this.currentEl);
26164             //Roo.log(this.currentEl.contains(dom));
26165             if (this.currentEl == el) {
26166                 return;
26167             }
26168             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26169                 return;
26170             }
26171
26172         }
26173         
26174         if (this.currentTip.el) {
26175             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26176         }    
26177         //Roo.log(ev);
26178         
26179         if(!el || el.dom == document){
26180             return;
26181         }
26182         
26183         var bindEl = el;
26184         
26185         // you can not look for children, as if el is the body.. then everythign is the child..
26186         if (!el.attr('tooltip')) { //
26187             if (!el.select("[tooltip]").elements.length) {
26188                 return;
26189             }
26190             // is the mouse over this child...?
26191             bindEl = el.select("[tooltip]").first();
26192             var xy = ev.getXY();
26193             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26194                 //Roo.log("not in region.");
26195                 return;
26196             }
26197             //Roo.log("child element over..");
26198             
26199         }
26200         this.currentEl = bindEl;
26201         this.currentTip.bind(bindEl);
26202         this.currentRegion = Roo.lib.Region.getRegion(dom);
26203         this.currentTip.enter();
26204         
26205     },
26206     leave : function(ev)
26207     {
26208         var dom = ev.getTarget();
26209         //Roo.log(['leave',dom]);
26210         if (!this.currentEl) {
26211             return;
26212         }
26213         
26214         
26215         if (dom != this.currentEl.dom) {
26216             return;
26217         }
26218         var xy = ev.getXY();
26219         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26220             return;
26221         }
26222         // only activate leave if mouse cursor is outside... bounding box..
26223         
26224         
26225         
26226         
26227         if (this.currentTip) {
26228             this.currentTip.leave();
26229         }
26230         //Roo.log('clear currentEl');
26231         this.currentEl = false;
26232         
26233         
26234     },
26235     alignment : {
26236         'left' : ['r-l', [-2,0], 'right'],
26237         'right' : ['l-r', [2,0], 'left'],
26238         'bottom' : ['t-b', [0,2], 'top'],
26239         'top' : [ 'b-t', [0,-2], 'bottom']
26240     }
26241     
26242 });
26243
26244
26245 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26246     
26247     
26248     bindEl : false,
26249     
26250     delay : null, // can be { show : 300 , hide: 500}
26251     
26252     timeout : null,
26253     
26254     hoverState : null, //???
26255     
26256     placement : 'bottom', 
26257     
26258     alignment : false,
26259     
26260     getAutoCreate : function(){
26261     
26262         var cfg = {
26263            cls : 'tooltip',
26264            role : 'tooltip',
26265            cn : [
26266                 {
26267                     cls : 'tooltip-arrow'
26268                 },
26269                 {
26270                     cls : 'tooltip-inner'
26271                 }
26272            ]
26273         };
26274         
26275         return cfg;
26276     },
26277     bind : function(el)
26278     {
26279         this.bindEl = el;
26280     },
26281       
26282     
26283     enter : function () {
26284        
26285         if (this.timeout != null) {
26286             clearTimeout(this.timeout);
26287         }
26288         
26289         this.hoverState = 'in';
26290          //Roo.log("enter - show");
26291         if (!this.delay || !this.delay.show) {
26292             this.show();
26293             return;
26294         }
26295         var _t = this;
26296         this.timeout = setTimeout(function () {
26297             if (_t.hoverState == 'in') {
26298                 _t.show();
26299             }
26300         }, this.delay.show);
26301     },
26302     leave : function()
26303     {
26304         clearTimeout(this.timeout);
26305     
26306         this.hoverState = 'out';
26307          if (!this.delay || !this.delay.hide) {
26308             this.hide();
26309             return;
26310         }
26311        
26312         var _t = this;
26313         this.timeout = setTimeout(function () {
26314             //Roo.log("leave - timeout");
26315             
26316             if (_t.hoverState == 'out') {
26317                 _t.hide();
26318                 Roo.bootstrap.Tooltip.currentEl = false;
26319             }
26320         }, delay);
26321     },
26322     
26323     show : function (msg)
26324     {
26325         if (!this.el) {
26326             this.render(document.body);
26327         }
26328         // set content.
26329         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26330         
26331         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26332         
26333         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26334         
26335         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26336         
26337         var placement = typeof this.placement == 'function' ?
26338             this.placement.call(this, this.el, on_el) :
26339             this.placement;
26340             
26341         var autoToken = /\s?auto?\s?/i;
26342         var autoPlace = autoToken.test(placement);
26343         if (autoPlace) {
26344             placement = placement.replace(autoToken, '') || 'top';
26345         }
26346         
26347         //this.el.detach()
26348         //this.el.setXY([0,0]);
26349         this.el.show();
26350         //this.el.dom.style.display='block';
26351         
26352         //this.el.appendTo(on_el);
26353         
26354         var p = this.getPosition();
26355         var box = this.el.getBox();
26356         
26357         if (autoPlace) {
26358             // fixme..
26359         }
26360         
26361         var align = this.alignment[placement];
26362         
26363         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26364         
26365         if(placement == 'top' || placement == 'bottom'){
26366             if(xy[0] < 0){
26367                 placement = 'right';
26368             }
26369             
26370             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26371                 placement = 'left';
26372             }
26373             
26374             var scroll = Roo.select('body', true).first().getScroll();
26375             
26376             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26377                 placement = 'top';
26378             }
26379             
26380             align = this.alignment[placement];
26381         }
26382         
26383         this.el.alignTo(this.bindEl, align[0],align[1]);
26384         //var arrow = this.el.select('.arrow',true).first();
26385         //arrow.set(align[2], 
26386         
26387         this.el.addClass(placement);
26388         
26389         this.el.addClass('in fade');
26390         
26391         this.hoverState = null;
26392         
26393         if (this.el.hasClass('fade')) {
26394             // fade it?
26395         }
26396         
26397     },
26398     hide : function()
26399     {
26400          
26401         if (!this.el) {
26402             return;
26403         }
26404         //this.el.setXY([0,0]);
26405         this.el.removeClass('in');
26406         //this.el.hide();
26407         
26408     }
26409     
26410 });
26411  
26412
26413  /*
26414  * - LGPL
26415  *
26416  * Location Picker
26417  * 
26418  */
26419
26420 /**
26421  * @class Roo.bootstrap.LocationPicker
26422  * @extends Roo.bootstrap.Component
26423  * Bootstrap LocationPicker class
26424  * @cfg {Number} latitude Position when init default 0
26425  * @cfg {Number} longitude Position when init default 0
26426  * @cfg {Number} zoom default 15
26427  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26428  * @cfg {Boolean} mapTypeControl default false
26429  * @cfg {Boolean} disableDoubleClickZoom default false
26430  * @cfg {Boolean} scrollwheel default true
26431  * @cfg {Boolean} streetViewControl default false
26432  * @cfg {Number} radius default 0
26433  * @cfg {String} locationName
26434  * @cfg {Boolean} draggable default true
26435  * @cfg {Boolean} enableAutocomplete default false
26436  * @cfg {Boolean} enableReverseGeocode default true
26437  * @cfg {String} markerTitle
26438  * 
26439  * @constructor
26440  * Create a new LocationPicker
26441  * @param {Object} config The config object
26442  */
26443
26444
26445 Roo.bootstrap.LocationPicker = function(config){
26446     
26447     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26448     
26449     this.addEvents({
26450         /**
26451          * @event initial
26452          * Fires when the picker initialized.
26453          * @param {Roo.bootstrap.LocationPicker} this
26454          * @param {Google Location} location
26455          */
26456         initial : true,
26457         /**
26458          * @event positionchanged
26459          * Fires when the picker position changed.
26460          * @param {Roo.bootstrap.LocationPicker} this
26461          * @param {Google Location} location
26462          */
26463         positionchanged : true,
26464         /**
26465          * @event resize
26466          * Fires when the map resize.
26467          * @param {Roo.bootstrap.LocationPicker} this
26468          */
26469         resize : true,
26470         /**
26471          * @event show
26472          * Fires when the map show.
26473          * @param {Roo.bootstrap.LocationPicker} this
26474          */
26475         show : true,
26476         /**
26477          * @event hide
26478          * Fires when the map hide.
26479          * @param {Roo.bootstrap.LocationPicker} this
26480          */
26481         hide : true,
26482         /**
26483          * @event mapClick
26484          * Fires when click the map.
26485          * @param {Roo.bootstrap.LocationPicker} this
26486          * @param {Map event} e
26487          */
26488         mapClick : true,
26489         /**
26490          * @event mapRightClick
26491          * Fires when right click the map.
26492          * @param {Roo.bootstrap.LocationPicker} this
26493          * @param {Map event} e
26494          */
26495         mapRightClick : true,
26496         /**
26497          * @event markerClick
26498          * Fires when click the marker.
26499          * @param {Roo.bootstrap.LocationPicker} this
26500          * @param {Map event} e
26501          */
26502         markerClick : true,
26503         /**
26504          * @event markerRightClick
26505          * Fires when right click the marker.
26506          * @param {Roo.bootstrap.LocationPicker} this
26507          * @param {Map event} e
26508          */
26509         markerRightClick : true,
26510         /**
26511          * @event OverlayViewDraw
26512          * Fires when OverlayView Draw
26513          * @param {Roo.bootstrap.LocationPicker} this
26514          */
26515         OverlayViewDraw : true,
26516         /**
26517          * @event OverlayViewOnAdd
26518          * Fires when OverlayView Draw
26519          * @param {Roo.bootstrap.LocationPicker} this
26520          */
26521         OverlayViewOnAdd : true,
26522         /**
26523          * @event OverlayViewOnRemove
26524          * Fires when OverlayView Draw
26525          * @param {Roo.bootstrap.LocationPicker} this
26526          */
26527         OverlayViewOnRemove : true,
26528         /**
26529          * @event OverlayViewShow
26530          * Fires when OverlayView Draw
26531          * @param {Roo.bootstrap.LocationPicker} this
26532          * @param {Pixel} cpx
26533          */
26534         OverlayViewShow : true,
26535         /**
26536          * @event OverlayViewHide
26537          * Fires when OverlayView Draw
26538          * @param {Roo.bootstrap.LocationPicker} this
26539          */
26540         OverlayViewHide : true,
26541         /**
26542          * @event loadexception
26543          * Fires when load google lib failed.
26544          * @param {Roo.bootstrap.LocationPicker} this
26545          */
26546         loadexception : true
26547     });
26548         
26549 };
26550
26551 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26552     
26553     gMapContext: false,
26554     
26555     latitude: 0,
26556     longitude: 0,
26557     zoom: 15,
26558     mapTypeId: false,
26559     mapTypeControl: false,
26560     disableDoubleClickZoom: false,
26561     scrollwheel: true,
26562     streetViewControl: false,
26563     radius: 0,
26564     locationName: '',
26565     draggable: true,
26566     enableAutocomplete: false,
26567     enableReverseGeocode: true,
26568     markerTitle: '',
26569     
26570     getAutoCreate: function()
26571     {
26572
26573         var cfg = {
26574             tag: 'div',
26575             cls: 'roo-location-picker'
26576         };
26577         
26578         return cfg
26579     },
26580     
26581     initEvents: function(ct, position)
26582     {       
26583         if(!this.el.getWidth() || this.isApplied()){
26584             return;
26585         }
26586         
26587         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26588         
26589         this.initial();
26590     },
26591     
26592     initial: function()
26593     {
26594         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26595             this.fireEvent('loadexception', this);
26596             return;
26597         }
26598         
26599         if(!this.mapTypeId){
26600             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26601         }
26602         
26603         this.gMapContext = this.GMapContext();
26604         
26605         this.initOverlayView();
26606         
26607         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26608         
26609         var _this = this;
26610                 
26611         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26612             _this.setPosition(_this.gMapContext.marker.position);
26613         });
26614         
26615         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26616             _this.fireEvent('mapClick', this, event);
26617             
26618         });
26619
26620         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26621             _this.fireEvent('mapRightClick', this, event);
26622             
26623         });
26624         
26625         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26626             _this.fireEvent('markerClick', this, event);
26627             
26628         });
26629
26630         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26631             _this.fireEvent('markerRightClick', this, event);
26632             
26633         });
26634         
26635         this.setPosition(this.gMapContext.location);
26636         
26637         this.fireEvent('initial', this, this.gMapContext.location);
26638     },
26639     
26640     initOverlayView: function()
26641     {
26642         var _this = this;
26643         
26644         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26645             
26646             draw: function()
26647             {
26648                 _this.fireEvent('OverlayViewDraw', _this);
26649             },
26650             
26651             onAdd: function()
26652             {
26653                 _this.fireEvent('OverlayViewOnAdd', _this);
26654             },
26655             
26656             onRemove: function()
26657             {
26658                 _this.fireEvent('OverlayViewOnRemove', _this);
26659             },
26660             
26661             show: function(cpx)
26662             {
26663                 _this.fireEvent('OverlayViewShow', _this, cpx);
26664             },
26665             
26666             hide: function()
26667             {
26668                 _this.fireEvent('OverlayViewHide', _this);
26669             }
26670             
26671         });
26672     },
26673     
26674     fromLatLngToContainerPixel: function(event)
26675     {
26676         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26677     },
26678     
26679     isApplied: function() 
26680     {
26681         return this.getGmapContext() == false ? false : true;
26682     },
26683     
26684     getGmapContext: function() 
26685     {
26686         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26687     },
26688     
26689     GMapContext: function() 
26690     {
26691         var position = new google.maps.LatLng(this.latitude, this.longitude);
26692         
26693         var _map = new google.maps.Map(this.el.dom, {
26694             center: position,
26695             zoom: this.zoom,
26696             mapTypeId: this.mapTypeId,
26697             mapTypeControl: this.mapTypeControl,
26698             disableDoubleClickZoom: this.disableDoubleClickZoom,
26699             scrollwheel: this.scrollwheel,
26700             streetViewControl: this.streetViewControl,
26701             locationName: this.locationName,
26702             draggable: this.draggable,
26703             enableAutocomplete: this.enableAutocomplete,
26704             enableReverseGeocode: this.enableReverseGeocode
26705         });
26706         
26707         var _marker = new google.maps.Marker({
26708             position: position,
26709             map: _map,
26710             title: this.markerTitle,
26711             draggable: this.draggable
26712         });
26713         
26714         return {
26715             map: _map,
26716             marker: _marker,
26717             circle: null,
26718             location: position,
26719             radius: this.radius,
26720             locationName: this.locationName,
26721             addressComponents: {
26722                 formatted_address: null,
26723                 addressLine1: null,
26724                 addressLine2: null,
26725                 streetName: null,
26726                 streetNumber: null,
26727                 city: null,
26728                 district: null,
26729                 state: null,
26730                 stateOrProvince: null
26731             },
26732             settings: this,
26733             domContainer: this.el.dom,
26734             geodecoder: new google.maps.Geocoder()
26735         };
26736     },
26737     
26738     drawCircle: function(center, radius, options) 
26739     {
26740         if (this.gMapContext.circle != null) {
26741             this.gMapContext.circle.setMap(null);
26742         }
26743         if (radius > 0) {
26744             radius *= 1;
26745             options = Roo.apply({}, options, {
26746                 strokeColor: "#0000FF",
26747                 strokeOpacity: .35,
26748                 strokeWeight: 2,
26749                 fillColor: "#0000FF",
26750                 fillOpacity: .2
26751             });
26752             
26753             options.map = this.gMapContext.map;
26754             options.radius = radius;
26755             options.center = center;
26756             this.gMapContext.circle = new google.maps.Circle(options);
26757             return this.gMapContext.circle;
26758         }
26759         
26760         return null;
26761     },
26762     
26763     setPosition: function(location) 
26764     {
26765         this.gMapContext.location = location;
26766         this.gMapContext.marker.setPosition(location);
26767         this.gMapContext.map.panTo(location);
26768         this.drawCircle(location, this.gMapContext.radius, {});
26769         
26770         var _this = this;
26771         
26772         if (this.gMapContext.settings.enableReverseGeocode) {
26773             this.gMapContext.geodecoder.geocode({
26774                 latLng: this.gMapContext.location
26775             }, function(results, status) {
26776                 
26777                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26778                     _this.gMapContext.locationName = results[0].formatted_address;
26779                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26780                     
26781                     _this.fireEvent('positionchanged', this, location);
26782                 }
26783             });
26784             
26785             return;
26786         }
26787         
26788         this.fireEvent('positionchanged', this, location);
26789     },
26790     
26791     resize: function()
26792     {
26793         google.maps.event.trigger(this.gMapContext.map, "resize");
26794         
26795         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26796         
26797         this.fireEvent('resize', this);
26798     },
26799     
26800     setPositionByLatLng: function(latitude, longitude)
26801     {
26802         this.setPosition(new google.maps.LatLng(latitude, longitude));
26803     },
26804     
26805     getCurrentPosition: function() 
26806     {
26807         return {
26808             latitude: this.gMapContext.location.lat(),
26809             longitude: this.gMapContext.location.lng()
26810         };
26811     },
26812     
26813     getAddressName: function() 
26814     {
26815         return this.gMapContext.locationName;
26816     },
26817     
26818     getAddressComponents: function() 
26819     {
26820         return this.gMapContext.addressComponents;
26821     },
26822     
26823     address_component_from_google_geocode: function(address_components) 
26824     {
26825         var result = {};
26826         
26827         for (var i = 0; i < address_components.length; i++) {
26828             var component = address_components[i];
26829             if (component.types.indexOf("postal_code") >= 0) {
26830                 result.postalCode = component.short_name;
26831             } else if (component.types.indexOf("street_number") >= 0) {
26832                 result.streetNumber = component.short_name;
26833             } else if (component.types.indexOf("route") >= 0) {
26834                 result.streetName = component.short_name;
26835             } else if (component.types.indexOf("neighborhood") >= 0) {
26836                 result.city = component.short_name;
26837             } else if (component.types.indexOf("locality") >= 0) {
26838                 result.city = component.short_name;
26839             } else if (component.types.indexOf("sublocality") >= 0) {
26840                 result.district = component.short_name;
26841             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26842                 result.stateOrProvince = component.short_name;
26843             } else if (component.types.indexOf("country") >= 0) {
26844                 result.country = component.short_name;
26845             }
26846         }
26847         
26848         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26849         result.addressLine2 = "";
26850         return result;
26851     },
26852     
26853     setZoomLevel: function(zoom)
26854     {
26855         this.gMapContext.map.setZoom(zoom);
26856     },
26857     
26858     show: function()
26859     {
26860         if(!this.el){
26861             return;
26862         }
26863         
26864         this.el.show();
26865         
26866         this.resize();
26867         
26868         this.fireEvent('show', this);
26869     },
26870     
26871     hide: function()
26872     {
26873         if(!this.el){
26874             return;
26875         }
26876         
26877         this.el.hide();
26878         
26879         this.fireEvent('hide', this);
26880     }
26881     
26882 });
26883
26884 Roo.apply(Roo.bootstrap.LocationPicker, {
26885     
26886     OverlayView : function(map, options)
26887     {
26888         options = options || {};
26889         
26890         this.setMap(map);
26891     }
26892     
26893     
26894 });/*
26895  * - LGPL
26896  *
26897  * Alert
26898  * 
26899  */
26900
26901 /**
26902  * @class Roo.bootstrap.Alert
26903  * @extends Roo.bootstrap.Component
26904  * Bootstrap Alert class
26905  * @cfg {String} title The title of alert
26906  * @cfg {String} html The content of alert
26907  * @cfg {String} weight (  success | info | warning | danger )
26908  * @cfg {String} faicon font-awesomeicon
26909  * 
26910  * @constructor
26911  * Create a new alert
26912  * @param {Object} config The config object
26913  */
26914
26915
26916 Roo.bootstrap.Alert = function(config){
26917     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26918     
26919 };
26920
26921 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26922     
26923     title: '',
26924     html: '',
26925     weight: false,
26926     faicon: false,
26927     
26928     getAutoCreate : function()
26929     {
26930         
26931         var cfg = {
26932             tag : 'div',
26933             cls : 'alert',
26934             cn : [
26935                 {
26936                     tag : 'i',
26937                     cls : 'roo-alert-icon'
26938                     
26939                 },
26940                 {
26941                     tag : 'b',
26942                     cls : 'roo-alert-title',
26943                     html : this.title
26944                 },
26945                 {
26946                     tag : 'span',
26947                     cls : 'roo-alert-text',
26948                     html : this.html
26949                 }
26950             ]
26951         };
26952         
26953         if(this.faicon){
26954             cfg.cn[0].cls += ' fa ' + this.faicon;
26955         }
26956         
26957         if(this.weight){
26958             cfg.cls += ' alert-' + this.weight;
26959         }
26960         
26961         return cfg;
26962     },
26963     
26964     initEvents: function() 
26965     {
26966         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26967     },
26968     
26969     setTitle : function(str)
26970     {
26971         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26972     },
26973     
26974     setText : function(str)
26975     {
26976         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26977     },
26978     
26979     setWeight : function(weight)
26980     {
26981         if(this.weight){
26982             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26983         }
26984         
26985         this.weight = weight;
26986         
26987         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26988     },
26989     
26990     setIcon : function(icon)
26991     {
26992         if(this.faicon){
26993             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26994         }
26995         
26996         this.faicon = icon;
26997         
26998         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26999     },
27000     
27001     hide: function() 
27002     {
27003         this.el.hide();   
27004     },
27005     
27006     show: function() 
27007     {  
27008         this.el.show();   
27009     }
27010     
27011 });
27012
27013  
27014 /*
27015 * Licence: LGPL
27016 */
27017
27018 /**
27019  * @class Roo.bootstrap.UploadCropbox
27020  * @extends Roo.bootstrap.Component
27021  * Bootstrap UploadCropbox class
27022  * @cfg {String} emptyText show when image has been loaded
27023  * @cfg {String} rotateNotify show when image too small to rotate
27024  * @cfg {Number} errorTimeout default 3000
27025  * @cfg {Number} minWidth default 300
27026  * @cfg {Number} minHeight default 300
27027  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27028  * @cfg {Boolean} isDocument (true|false) default false
27029  * @cfg {String} url action url
27030  * @cfg {String} paramName default 'imageUpload'
27031  * @cfg {String} method default POST
27032  * @cfg {Boolean} loadMask (true|false) default true
27033  * @cfg {Boolean} loadingText default 'Loading...'
27034  * 
27035  * @constructor
27036  * Create a new UploadCropbox
27037  * @param {Object} config The config object
27038  */
27039
27040 Roo.bootstrap.UploadCropbox = function(config){
27041     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27042     
27043     this.addEvents({
27044         /**
27045          * @event beforeselectfile
27046          * Fire before select file
27047          * @param {Roo.bootstrap.UploadCropbox} this
27048          */
27049         "beforeselectfile" : true,
27050         /**
27051          * @event initial
27052          * Fire after initEvent
27053          * @param {Roo.bootstrap.UploadCropbox} this
27054          */
27055         "initial" : true,
27056         /**
27057          * @event crop
27058          * Fire after initEvent
27059          * @param {Roo.bootstrap.UploadCropbox} this
27060          * @param {String} data
27061          */
27062         "crop" : true,
27063         /**
27064          * @event prepare
27065          * Fire when preparing the file data
27066          * @param {Roo.bootstrap.UploadCropbox} this
27067          * @param {Object} file
27068          */
27069         "prepare" : true,
27070         /**
27071          * @event exception
27072          * Fire when get exception
27073          * @param {Roo.bootstrap.UploadCropbox} this
27074          * @param {XMLHttpRequest} xhr
27075          */
27076         "exception" : true,
27077         /**
27078          * @event beforeloadcanvas
27079          * Fire before load the canvas
27080          * @param {Roo.bootstrap.UploadCropbox} this
27081          * @param {String} src
27082          */
27083         "beforeloadcanvas" : true,
27084         /**
27085          * @event trash
27086          * Fire when trash image
27087          * @param {Roo.bootstrap.UploadCropbox} this
27088          */
27089         "trash" : true,
27090         /**
27091          * @event download
27092          * Fire when download the image
27093          * @param {Roo.bootstrap.UploadCropbox} this
27094          */
27095         "download" : true,
27096         /**
27097          * @event footerbuttonclick
27098          * Fire when footerbuttonclick
27099          * @param {Roo.bootstrap.UploadCropbox} this
27100          * @param {String} type
27101          */
27102         "footerbuttonclick" : true,
27103         /**
27104          * @event resize
27105          * Fire when resize
27106          * @param {Roo.bootstrap.UploadCropbox} this
27107          */
27108         "resize" : true,
27109         /**
27110          * @event rotate
27111          * Fire when rotate the image
27112          * @param {Roo.bootstrap.UploadCropbox} this
27113          * @param {String} pos
27114          */
27115         "rotate" : true,
27116         /**
27117          * @event inspect
27118          * Fire when inspect the file
27119          * @param {Roo.bootstrap.UploadCropbox} this
27120          * @param {Object} file
27121          */
27122         "inspect" : true,
27123         /**
27124          * @event upload
27125          * Fire when xhr upload the file
27126          * @param {Roo.bootstrap.UploadCropbox} this
27127          * @param {Object} data
27128          */
27129         "upload" : true,
27130         /**
27131          * @event arrange
27132          * Fire when arrange the file data
27133          * @param {Roo.bootstrap.UploadCropbox} this
27134          * @param {Object} formData
27135          */
27136         "arrange" : true
27137     });
27138     
27139     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27140 };
27141
27142 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27143     
27144     emptyText : 'Click to upload image',
27145     rotateNotify : 'Image is too small to rotate',
27146     errorTimeout : 3000,
27147     scale : 0,
27148     baseScale : 1,
27149     rotate : 0,
27150     dragable : false,
27151     pinching : false,
27152     mouseX : 0,
27153     mouseY : 0,
27154     cropData : false,
27155     minWidth : 300,
27156     minHeight : 300,
27157     file : false,
27158     exif : {},
27159     baseRotate : 1,
27160     cropType : 'image/jpeg',
27161     buttons : false,
27162     canvasLoaded : false,
27163     isDocument : false,
27164     method : 'POST',
27165     paramName : 'imageUpload',
27166     loadMask : true,
27167     loadingText : 'Loading...',
27168     maskEl : false,
27169     
27170     getAutoCreate : function()
27171     {
27172         var cfg = {
27173             tag : 'div',
27174             cls : 'roo-upload-cropbox',
27175             cn : [
27176                 {
27177                     tag : 'input',
27178                     cls : 'roo-upload-cropbox-selector',
27179                     type : 'file'
27180                 },
27181                 {
27182                     tag : 'div',
27183                     cls : 'roo-upload-cropbox-body',
27184                     style : 'cursor:pointer',
27185                     cn : [
27186                         {
27187                             tag : 'div',
27188                             cls : 'roo-upload-cropbox-preview'
27189                         },
27190                         {
27191                             tag : 'div',
27192                             cls : 'roo-upload-cropbox-thumb'
27193                         },
27194                         {
27195                             tag : 'div',
27196                             cls : 'roo-upload-cropbox-empty-notify',
27197                             html : this.emptyText
27198                         },
27199                         {
27200                             tag : 'div',
27201                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27202                             html : this.rotateNotify
27203                         }
27204                     ]
27205                 },
27206                 {
27207                     tag : 'div',
27208                     cls : 'roo-upload-cropbox-footer',
27209                     cn : {
27210                         tag : 'div',
27211                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27212                         cn : []
27213                     }
27214                 }
27215             ]
27216         };
27217         
27218         return cfg;
27219     },
27220     
27221     onRender : function(ct, position)
27222     {
27223         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27224         
27225         if (this.buttons.length) {
27226             
27227             Roo.each(this.buttons, function(bb) {
27228                 
27229                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27230                 
27231                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27232                 
27233             }, this);
27234         }
27235         
27236         if(this.loadMask){
27237             this.maskEl = this.el;
27238         }
27239     },
27240     
27241     initEvents : function()
27242     {
27243         this.urlAPI = (window.createObjectURL && window) || 
27244                                 (window.URL && URL.revokeObjectURL && URL) || 
27245                                 (window.webkitURL && webkitURL);
27246                         
27247         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27248         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27249         
27250         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27251         this.selectorEl.hide();
27252         
27253         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27254         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27255         
27256         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27257         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27258         this.thumbEl.hide();
27259         
27260         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27261         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27262         
27263         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27264         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27265         this.errorEl.hide();
27266         
27267         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27268         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27269         this.footerEl.hide();
27270         
27271         this.setThumbBoxSize();
27272         
27273         this.bind();
27274         
27275         this.resize();
27276         
27277         this.fireEvent('initial', this);
27278     },
27279
27280     bind : function()
27281     {
27282         var _this = this;
27283         
27284         window.addEventListener("resize", function() { _this.resize(); } );
27285         
27286         this.bodyEl.on('click', this.beforeSelectFile, this);
27287         
27288         if(Roo.isTouch){
27289             this.bodyEl.on('touchstart', this.onTouchStart, this);
27290             this.bodyEl.on('touchmove', this.onTouchMove, this);
27291             this.bodyEl.on('touchend', this.onTouchEnd, this);
27292         }
27293         
27294         if(!Roo.isTouch){
27295             this.bodyEl.on('mousedown', this.onMouseDown, this);
27296             this.bodyEl.on('mousemove', this.onMouseMove, this);
27297             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27298             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27299             Roo.get(document).on('mouseup', this.onMouseUp, this);
27300         }
27301         
27302         this.selectorEl.on('change', this.onFileSelected, this);
27303     },
27304     
27305     reset : function()
27306     {    
27307         this.scale = 0;
27308         this.baseScale = 1;
27309         this.rotate = 0;
27310         this.baseRotate = 1;
27311         this.dragable = false;
27312         this.pinching = false;
27313         this.mouseX = 0;
27314         this.mouseY = 0;
27315         this.cropData = false;
27316         this.notifyEl.dom.innerHTML = this.emptyText;
27317         
27318         this.selectorEl.dom.value = '';
27319         
27320     },
27321     
27322     resize : function()
27323     {
27324         if(this.fireEvent('resize', this) != false){
27325             this.setThumbBoxPosition();
27326             this.setCanvasPosition();
27327         }
27328     },
27329     
27330     onFooterButtonClick : function(e, el, o, type)
27331     {
27332         switch (type) {
27333             case 'rotate-left' :
27334                 this.onRotateLeft(e);
27335                 break;
27336             case 'rotate-right' :
27337                 this.onRotateRight(e);
27338                 break;
27339             case 'picture' :
27340                 this.beforeSelectFile(e);
27341                 break;
27342             case 'trash' :
27343                 this.trash(e);
27344                 break;
27345             case 'crop' :
27346                 this.crop(e);
27347                 break;
27348             case 'download' :
27349                 this.download(e);
27350                 break;
27351             default :
27352                 break;
27353         }
27354         
27355         this.fireEvent('footerbuttonclick', this, type);
27356     },
27357     
27358     beforeSelectFile : function(e)
27359     {
27360         e.preventDefault();
27361         
27362         if(this.fireEvent('beforeselectfile', this) != false){
27363             this.selectorEl.dom.click();
27364         }
27365     },
27366     
27367     onFileSelected : function(e)
27368     {
27369         e.preventDefault();
27370         
27371         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27372             return;
27373         }
27374         
27375         var file = this.selectorEl.dom.files[0];
27376         
27377         if(this.fireEvent('inspect', this, file) != false){
27378             this.prepare(file);
27379         }
27380         
27381     },
27382     
27383     trash : function(e)
27384     {
27385         this.fireEvent('trash', this);
27386     },
27387     
27388     download : function(e)
27389     {
27390         this.fireEvent('download', this);
27391     },
27392     
27393     loadCanvas : function(src)
27394     {   
27395         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27396             
27397             this.reset();
27398             
27399             this.imageEl = document.createElement('img');
27400             
27401             var _this = this;
27402             
27403             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27404             
27405             this.imageEl.src = src;
27406         }
27407     },
27408     
27409     onLoadCanvas : function()
27410     {   
27411         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27412         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27413         
27414         this.bodyEl.un('click', this.beforeSelectFile, this);
27415         
27416         this.notifyEl.hide();
27417         this.thumbEl.show();
27418         this.footerEl.show();
27419         
27420         this.baseRotateLevel();
27421         
27422         if(this.isDocument){
27423             this.setThumbBoxSize();
27424         }
27425         
27426         this.setThumbBoxPosition();
27427         
27428         this.baseScaleLevel();
27429         
27430         this.draw();
27431         
27432         this.resize();
27433         
27434         this.canvasLoaded = true;
27435         
27436         if(this.loadMask){
27437             this.maskEl.unmask();
27438         }
27439         
27440     },
27441     
27442     setCanvasPosition : function()
27443     {   
27444         if(!this.canvasEl){
27445             return;
27446         }
27447         
27448         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27449         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27450         
27451         this.previewEl.setLeft(pw);
27452         this.previewEl.setTop(ph);
27453         
27454     },
27455     
27456     onMouseDown : function(e)
27457     {   
27458         e.stopEvent();
27459         
27460         this.dragable = true;
27461         this.pinching = false;
27462         
27463         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27464             this.dragable = false;
27465             return;
27466         }
27467         
27468         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27469         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27470         
27471     },
27472     
27473     onMouseMove : function(e)
27474     {   
27475         e.stopEvent();
27476         
27477         if(!this.canvasLoaded){
27478             return;
27479         }
27480         
27481         if (!this.dragable){
27482             return;
27483         }
27484         
27485         var minX = Math.ceil(this.thumbEl.getLeft(true));
27486         var minY = Math.ceil(this.thumbEl.getTop(true));
27487         
27488         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27489         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27490         
27491         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27492         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27493         
27494         x = x - this.mouseX;
27495         y = y - this.mouseY;
27496         
27497         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27498         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27499         
27500         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27501         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27502         
27503         this.previewEl.setLeft(bgX);
27504         this.previewEl.setTop(bgY);
27505         
27506         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27507         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27508     },
27509     
27510     onMouseUp : function(e)
27511     {   
27512         e.stopEvent();
27513         
27514         this.dragable = false;
27515     },
27516     
27517     onMouseWheel : function(e)
27518     {   
27519         e.stopEvent();
27520         
27521         this.startScale = this.scale;
27522         
27523         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27524         
27525         if(!this.zoomable()){
27526             this.scale = this.startScale;
27527             return;
27528         }
27529         
27530         this.draw();
27531         
27532         return;
27533     },
27534     
27535     zoomable : function()
27536     {
27537         var minScale = this.thumbEl.getWidth() / this.minWidth;
27538         
27539         if(this.minWidth < this.minHeight){
27540             minScale = this.thumbEl.getHeight() / this.minHeight;
27541         }
27542         
27543         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27544         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27545         
27546         if(
27547                 this.isDocument &&
27548                 (this.rotate == 0 || this.rotate == 180) && 
27549                 (
27550                     width > this.imageEl.OriginWidth || 
27551                     height > this.imageEl.OriginHeight ||
27552                     (width < this.minWidth && height < this.minHeight)
27553                 )
27554         ){
27555             return false;
27556         }
27557         
27558         if(
27559                 this.isDocument &&
27560                 (this.rotate == 90 || this.rotate == 270) && 
27561                 (
27562                     width > this.imageEl.OriginWidth || 
27563                     height > this.imageEl.OriginHeight ||
27564                     (width < this.minHeight && height < this.minWidth)
27565                 )
27566         ){
27567             return false;
27568         }
27569         
27570         if(
27571                 !this.isDocument &&
27572                 (this.rotate == 0 || this.rotate == 180) && 
27573                 (
27574                     width < this.minWidth || 
27575                     width > this.imageEl.OriginWidth || 
27576                     height < this.minHeight || 
27577                     height > this.imageEl.OriginHeight
27578                 )
27579         ){
27580             return false;
27581         }
27582         
27583         if(
27584                 !this.isDocument &&
27585                 (this.rotate == 90 || this.rotate == 270) && 
27586                 (
27587                     width < this.minHeight || 
27588                     width > this.imageEl.OriginWidth || 
27589                     height < this.minWidth || 
27590                     height > this.imageEl.OriginHeight
27591                 )
27592         ){
27593             return false;
27594         }
27595         
27596         return true;
27597         
27598     },
27599     
27600     onRotateLeft : function(e)
27601     {   
27602         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27603             
27604             var minScale = this.thumbEl.getWidth() / this.minWidth;
27605             
27606             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27607             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27608             
27609             this.startScale = this.scale;
27610             
27611             while (this.getScaleLevel() < minScale){
27612             
27613                 this.scale = this.scale + 1;
27614                 
27615                 if(!this.zoomable()){
27616                     break;
27617                 }
27618                 
27619                 if(
27620                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27621                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27622                 ){
27623                     continue;
27624                 }
27625                 
27626                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27627
27628                 this.draw();
27629                 
27630                 return;
27631             }
27632             
27633             this.scale = this.startScale;
27634             
27635             this.onRotateFail();
27636             
27637             return false;
27638         }
27639         
27640         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27641
27642         if(this.isDocument){
27643             this.setThumbBoxSize();
27644             this.setThumbBoxPosition();
27645             this.setCanvasPosition();
27646         }
27647         
27648         this.draw();
27649         
27650         this.fireEvent('rotate', this, 'left');
27651         
27652     },
27653     
27654     onRotateRight : function(e)
27655     {
27656         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27657             
27658             var minScale = this.thumbEl.getWidth() / this.minWidth;
27659         
27660             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27661             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27662             
27663             this.startScale = this.scale;
27664             
27665             while (this.getScaleLevel() < minScale){
27666             
27667                 this.scale = this.scale + 1;
27668                 
27669                 if(!this.zoomable()){
27670                     break;
27671                 }
27672                 
27673                 if(
27674                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27675                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27676                 ){
27677                     continue;
27678                 }
27679                 
27680                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27681
27682                 this.draw();
27683                 
27684                 return;
27685             }
27686             
27687             this.scale = this.startScale;
27688             
27689             this.onRotateFail();
27690             
27691             return false;
27692         }
27693         
27694         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27695
27696         if(this.isDocument){
27697             this.setThumbBoxSize();
27698             this.setThumbBoxPosition();
27699             this.setCanvasPosition();
27700         }
27701         
27702         this.draw();
27703         
27704         this.fireEvent('rotate', this, 'right');
27705     },
27706     
27707     onRotateFail : function()
27708     {
27709         this.errorEl.show(true);
27710         
27711         var _this = this;
27712         
27713         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27714     },
27715     
27716     draw : function()
27717     {
27718         this.previewEl.dom.innerHTML = '';
27719         
27720         var canvasEl = document.createElement("canvas");
27721         
27722         var contextEl = canvasEl.getContext("2d");
27723         
27724         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27725         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27726         var center = this.imageEl.OriginWidth / 2;
27727         
27728         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27729             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27730             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27731             center = this.imageEl.OriginHeight / 2;
27732         }
27733         
27734         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27735         
27736         contextEl.translate(center, center);
27737         contextEl.rotate(this.rotate * Math.PI / 180);
27738
27739         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27740         
27741         this.canvasEl = document.createElement("canvas");
27742         
27743         this.contextEl = this.canvasEl.getContext("2d");
27744         
27745         switch (this.rotate) {
27746             case 0 :
27747                 
27748                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27749                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27750                 
27751                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27752                 
27753                 break;
27754             case 90 : 
27755                 
27756                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27757                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27758                 
27759                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27760                     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);
27761                     break;
27762                 }
27763                 
27764                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27765                 
27766                 break;
27767             case 180 :
27768                 
27769                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27770                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27771                 
27772                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27773                     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);
27774                     break;
27775                 }
27776                 
27777                 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);
27778                 
27779                 break;
27780             case 270 :
27781                 
27782                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27783                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27784         
27785                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27786                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27787                     break;
27788                 }
27789                 
27790                 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);
27791                 
27792                 break;
27793             default : 
27794                 break;
27795         }
27796         
27797         this.previewEl.appendChild(this.canvasEl);
27798         
27799         this.setCanvasPosition();
27800     },
27801     
27802     crop : function()
27803     {
27804         if(!this.canvasLoaded){
27805             return;
27806         }
27807         
27808         var imageCanvas = document.createElement("canvas");
27809         
27810         var imageContext = imageCanvas.getContext("2d");
27811         
27812         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27813         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27814         
27815         var center = imageCanvas.width / 2;
27816         
27817         imageContext.translate(center, center);
27818         
27819         imageContext.rotate(this.rotate * Math.PI / 180);
27820         
27821         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27822         
27823         var canvas = document.createElement("canvas");
27824         
27825         var context = canvas.getContext("2d");
27826                 
27827         canvas.width = this.minWidth;
27828         canvas.height = this.minHeight;
27829
27830         switch (this.rotate) {
27831             case 0 :
27832                 
27833                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27834                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27835                 
27836                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27837                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27838                 
27839                 var targetWidth = this.minWidth - 2 * x;
27840                 var targetHeight = this.minHeight - 2 * y;
27841                 
27842                 var scale = 1;
27843                 
27844                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27845                     scale = targetWidth / width;
27846                 }
27847                 
27848                 if(x > 0 && y == 0){
27849                     scale = targetHeight / height;
27850                 }
27851                 
27852                 if(x > 0 && y > 0){
27853                     scale = targetWidth / width;
27854                     
27855                     if(width < height){
27856                         scale = targetHeight / height;
27857                     }
27858                 }
27859                 
27860                 context.scale(scale, scale);
27861                 
27862                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27863                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27864
27865                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27866                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27867
27868                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27869                 
27870                 break;
27871             case 90 : 
27872                 
27873                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27874                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27875                 
27876                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27877                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27878                 
27879                 var targetWidth = this.minWidth - 2 * x;
27880                 var targetHeight = this.minHeight - 2 * y;
27881                 
27882                 var scale = 1;
27883                 
27884                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27885                     scale = targetWidth / width;
27886                 }
27887                 
27888                 if(x > 0 && y == 0){
27889                     scale = targetHeight / height;
27890                 }
27891                 
27892                 if(x > 0 && y > 0){
27893                     scale = targetWidth / width;
27894                     
27895                     if(width < height){
27896                         scale = targetHeight / height;
27897                     }
27898                 }
27899                 
27900                 context.scale(scale, scale);
27901                 
27902                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27903                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27904
27905                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27906                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27907                 
27908                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27909                 
27910                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27911                 
27912                 break;
27913             case 180 :
27914                 
27915                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27916                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27917                 
27918                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27919                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27920                 
27921                 var targetWidth = this.minWidth - 2 * x;
27922                 var targetHeight = this.minHeight - 2 * y;
27923                 
27924                 var scale = 1;
27925                 
27926                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27927                     scale = targetWidth / width;
27928                 }
27929                 
27930                 if(x > 0 && y == 0){
27931                     scale = targetHeight / height;
27932                 }
27933                 
27934                 if(x > 0 && y > 0){
27935                     scale = targetWidth / width;
27936                     
27937                     if(width < height){
27938                         scale = targetHeight / height;
27939                     }
27940                 }
27941                 
27942                 context.scale(scale, scale);
27943                 
27944                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27945                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27946
27947                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27948                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27949
27950                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27951                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27952                 
27953                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27954                 
27955                 break;
27956             case 270 :
27957                 
27958                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27959                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27960                 
27961                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27962                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27963                 
27964                 var targetWidth = this.minWidth - 2 * x;
27965                 var targetHeight = this.minHeight - 2 * y;
27966                 
27967                 var scale = 1;
27968                 
27969                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27970                     scale = targetWidth / width;
27971                 }
27972                 
27973                 if(x > 0 && y == 0){
27974                     scale = targetHeight / height;
27975                 }
27976                 
27977                 if(x > 0 && y > 0){
27978                     scale = targetWidth / width;
27979                     
27980                     if(width < height){
27981                         scale = targetHeight / height;
27982                     }
27983                 }
27984                 
27985                 context.scale(scale, scale);
27986                 
27987                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27988                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27989
27990                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27991                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27992                 
27993                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27994                 
27995                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27996                 
27997                 break;
27998             default : 
27999                 break;
28000         }
28001         
28002         this.cropData = canvas.toDataURL(this.cropType);
28003         
28004         if(this.fireEvent('crop', this, this.cropData) !== false){
28005             this.process(this.file, this.cropData);
28006         }
28007         
28008         return;
28009         
28010     },
28011     
28012     setThumbBoxSize : function()
28013     {
28014         var width, height;
28015         
28016         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28017             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28018             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28019             
28020             this.minWidth = width;
28021             this.minHeight = height;
28022             
28023             if(this.rotate == 90 || this.rotate == 270){
28024                 this.minWidth = height;
28025                 this.minHeight = width;
28026             }
28027         }
28028         
28029         height = 300;
28030         width = Math.ceil(this.minWidth * height / this.minHeight);
28031         
28032         if(this.minWidth > this.minHeight){
28033             width = 300;
28034             height = Math.ceil(this.minHeight * width / this.minWidth);
28035         }
28036         
28037         this.thumbEl.setStyle({
28038             width : width + 'px',
28039             height : height + 'px'
28040         });
28041
28042         return;
28043             
28044     },
28045     
28046     setThumbBoxPosition : function()
28047     {
28048         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28049         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28050         
28051         this.thumbEl.setLeft(x);
28052         this.thumbEl.setTop(y);
28053         
28054     },
28055     
28056     baseRotateLevel : function()
28057     {
28058         this.baseRotate = 1;
28059         
28060         if(
28061                 typeof(this.exif) != 'undefined' &&
28062                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28063                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28064         ){
28065             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28066         }
28067         
28068         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28069         
28070     },
28071     
28072     baseScaleLevel : function()
28073     {
28074         var width, height;
28075         
28076         if(this.isDocument){
28077             
28078             if(this.baseRotate == 6 || this.baseRotate == 8){
28079             
28080                 height = this.thumbEl.getHeight();
28081                 this.baseScale = height / this.imageEl.OriginWidth;
28082
28083                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28084                     width = this.thumbEl.getWidth();
28085                     this.baseScale = width / this.imageEl.OriginHeight;
28086                 }
28087
28088                 return;
28089             }
28090
28091             height = this.thumbEl.getHeight();
28092             this.baseScale = height / this.imageEl.OriginHeight;
28093
28094             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28095                 width = this.thumbEl.getWidth();
28096                 this.baseScale = width / this.imageEl.OriginWidth;
28097             }
28098
28099             return;
28100         }
28101         
28102         if(this.baseRotate == 6 || this.baseRotate == 8){
28103             
28104             width = this.thumbEl.getHeight();
28105             this.baseScale = width / this.imageEl.OriginHeight;
28106             
28107             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28108                 height = this.thumbEl.getWidth();
28109                 this.baseScale = height / this.imageEl.OriginHeight;
28110             }
28111             
28112             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28113                 height = this.thumbEl.getWidth();
28114                 this.baseScale = height / this.imageEl.OriginHeight;
28115                 
28116                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28117                     width = this.thumbEl.getHeight();
28118                     this.baseScale = width / this.imageEl.OriginWidth;
28119                 }
28120             }
28121             
28122             return;
28123         }
28124         
28125         width = this.thumbEl.getWidth();
28126         this.baseScale = width / this.imageEl.OriginWidth;
28127         
28128         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28129             height = this.thumbEl.getHeight();
28130             this.baseScale = height / this.imageEl.OriginHeight;
28131         }
28132         
28133         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28134             
28135             height = this.thumbEl.getHeight();
28136             this.baseScale = height / this.imageEl.OriginHeight;
28137             
28138             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28139                 width = this.thumbEl.getWidth();
28140                 this.baseScale = width / this.imageEl.OriginWidth;
28141             }
28142             
28143         }
28144         
28145         return;
28146     },
28147     
28148     getScaleLevel : function()
28149     {
28150         return this.baseScale * Math.pow(1.1, this.scale);
28151     },
28152     
28153     onTouchStart : function(e)
28154     {
28155         if(!this.canvasLoaded){
28156             this.beforeSelectFile(e);
28157             return;
28158         }
28159         
28160         var touches = e.browserEvent.touches;
28161         
28162         if(!touches){
28163             return;
28164         }
28165         
28166         if(touches.length == 1){
28167             this.onMouseDown(e);
28168             return;
28169         }
28170         
28171         if(touches.length != 2){
28172             return;
28173         }
28174         
28175         var coords = [];
28176         
28177         for(var i = 0, finger; finger = touches[i]; i++){
28178             coords.push(finger.pageX, finger.pageY);
28179         }
28180         
28181         var x = Math.pow(coords[0] - coords[2], 2);
28182         var y = Math.pow(coords[1] - coords[3], 2);
28183         
28184         this.startDistance = Math.sqrt(x + y);
28185         
28186         this.startScale = this.scale;
28187         
28188         this.pinching = true;
28189         this.dragable = false;
28190         
28191     },
28192     
28193     onTouchMove : function(e)
28194     {
28195         if(!this.pinching && !this.dragable){
28196             return;
28197         }
28198         
28199         var touches = e.browserEvent.touches;
28200         
28201         if(!touches){
28202             return;
28203         }
28204         
28205         if(this.dragable){
28206             this.onMouseMove(e);
28207             return;
28208         }
28209         
28210         var coords = [];
28211         
28212         for(var i = 0, finger; finger = touches[i]; i++){
28213             coords.push(finger.pageX, finger.pageY);
28214         }
28215         
28216         var x = Math.pow(coords[0] - coords[2], 2);
28217         var y = Math.pow(coords[1] - coords[3], 2);
28218         
28219         this.endDistance = Math.sqrt(x + y);
28220         
28221         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28222         
28223         if(!this.zoomable()){
28224             this.scale = this.startScale;
28225             return;
28226         }
28227         
28228         this.draw();
28229         
28230     },
28231     
28232     onTouchEnd : function(e)
28233     {
28234         this.pinching = false;
28235         this.dragable = false;
28236         
28237     },
28238     
28239     process : function(file, crop)
28240     {
28241         if(this.loadMask){
28242             this.maskEl.mask(this.loadingText);
28243         }
28244         
28245         this.xhr = new XMLHttpRequest();
28246         
28247         file.xhr = this.xhr;
28248
28249         this.xhr.open(this.method, this.url, true);
28250         
28251         var headers = {
28252             "Accept": "application/json",
28253             "Cache-Control": "no-cache",
28254             "X-Requested-With": "XMLHttpRequest"
28255         };
28256         
28257         for (var headerName in headers) {
28258             var headerValue = headers[headerName];
28259             if (headerValue) {
28260                 this.xhr.setRequestHeader(headerName, headerValue);
28261             }
28262         }
28263         
28264         var _this = this;
28265         
28266         this.xhr.onload = function()
28267         {
28268             _this.xhrOnLoad(_this.xhr);
28269         }
28270         
28271         this.xhr.onerror = function()
28272         {
28273             _this.xhrOnError(_this.xhr);
28274         }
28275         
28276         var formData = new FormData();
28277
28278         formData.append('returnHTML', 'NO');
28279         
28280         if(crop){
28281             formData.append('crop', crop);
28282         }
28283         
28284         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28285             formData.append(this.paramName, file, file.name);
28286         }
28287         
28288         if(typeof(file.filename) != 'undefined'){
28289             formData.append('filename', file.filename);
28290         }
28291         
28292         if(typeof(file.mimetype) != 'undefined'){
28293             formData.append('mimetype', file.mimetype);
28294         }
28295         
28296         if(this.fireEvent('arrange', this, formData) != false){
28297             this.xhr.send(formData);
28298         };
28299     },
28300     
28301     xhrOnLoad : function(xhr)
28302     {
28303         if(this.loadMask){
28304             this.maskEl.unmask();
28305         }
28306         
28307         if (xhr.readyState !== 4) {
28308             this.fireEvent('exception', this, xhr);
28309             return;
28310         }
28311
28312         var response = Roo.decode(xhr.responseText);
28313         
28314         if(!response.success){
28315             this.fireEvent('exception', this, xhr);
28316             return;
28317         }
28318         
28319         var response = Roo.decode(xhr.responseText);
28320         
28321         this.fireEvent('upload', this, response);
28322         
28323     },
28324     
28325     xhrOnError : function()
28326     {
28327         if(this.loadMask){
28328             this.maskEl.unmask();
28329         }
28330         
28331         Roo.log('xhr on error');
28332         
28333         var response = Roo.decode(xhr.responseText);
28334           
28335         Roo.log(response);
28336         
28337     },
28338     
28339     prepare : function(file)
28340     {   
28341         if(this.loadMask){
28342             this.maskEl.mask(this.loadingText);
28343         }
28344         
28345         this.file = false;
28346         this.exif = {};
28347         
28348         if(typeof(file) === 'string'){
28349             this.loadCanvas(file);
28350             return;
28351         }
28352         
28353         if(!file || !this.urlAPI){
28354             return;
28355         }
28356         
28357         this.file = file;
28358         this.cropType = file.type;
28359         
28360         var _this = this;
28361         
28362         if(this.fireEvent('prepare', this, this.file) != false){
28363             
28364             var reader = new FileReader();
28365             
28366             reader.onload = function (e) {
28367                 if (e.target.error) {
28368                     Roo.log(e.target.error);
28369                     return;
28370                 }
28371                 
28372                 var buffer = e.target.result,
28373                     dataView = new DataView(buffer),
28374                     offset = 2,
28375                     maxOffset = dataView.byteLength - 4,
28376                     markerBytes,
28377                     markerLength;
28378                 
28379                 if (dataView.getUint16(0) === 0xffd8) {
28380                     while (offset < maxOffset) {
28381                         markerBytes = dataView.getUint16(offset);
28382                         
28383                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28384                             markerLength = dataView.getUint16(offset + 2) + 2;
28385                             if (offset + markerLength > dataView.byteLength) {
28386                                 Roo.log('Invalid meta data: Invalid segment size.');
28387                                 break;
28388                             }
28389                             
28390                             if(markerBytes == 0xffe1){
28391                                 _this.parseExifData(
28392                                     dataView,
28393                                     offset,
28394                                     markerLength
28395                                 );
28396                             }
28397                             
28398                             offset += markerLength;
28399                             
28400                             continue;
28401                         }
28402                         
28403                         break;
28404                     }
28405                     
28406                 }
28407                 
28408                 var url = _this.urlAPI.createObjectURL(_this.file);
28409                 
28410                 _this.loadCanvas(url);
28411                 
28412                 return;
28413             }
28414             
28415             reader.readAsArrayBuffer(this.file);
28416             
28417         }
28418         
28419     },
28420     
28421     parseExifData : function(dataView, offset, length)
28422     {
28423         var tiffOffset = offset + 10,
28424             littleEndian,
28425             dirOffset;
28426     
28427         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28428             // No Exif data, might be XMP data instead
28429             return;
28430         }
28431         
28432         // Check for the ASCII code for "Exif" (0x45786966):
28433         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28434             // No Exif data, might be XMP data instead
28435             return;
28436         }
28437         if (tiffOffset + 8 > dataView.byteLength) {
28438             Roo.log('Invalid Exif data: Invalid segment size.');
28439             return;
28440         }
28441         // Check for the two null bytes:
28442         if (dataView.getUint16(offset + 8) !== 0x0000) {
28443             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28444             return;
28445         }
28446         // Check the byte alignment:
28447         switch (dataView.getUint16(tiffOffset)) {
28448         case 0x4949:
28449             littleEndian = true;
28450             break;
28451         case 0x4D4D:
28452             littleEndian = false;
28453             break;
28454         default:
28455             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28456             return;
28457         }
28458         // Check for the TIFF tag marker (0x002A):
28459         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28460             Roo.log('Invalid Exif data: Missing TIFF marker.');
28461             return;
28462         }
28463         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28464         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28465         
28466         this.parseExifTags(
28467             dataView,
28468             tiffOffset,
28469             tiffOffset + dirOffset,
28470             littleEndian
28471         );
28472     },
28473     
28474     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28475     {
28476         var tagsNumber,
28477             dirEndOffset,
28478             i;
28479         if (dirOffset + 6 > dataView.byteLength) {
28480             Roo.log('Invalid Exif data: Invalid directory offset.');
28481             return;
28482         }
28483         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28484         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28485         if (dirEndOffset + 4 > dataView.byteLength) {
28486             Roo.log('Invalid Exif data: Invalid directory size.');
28487             return;
28488         }
28489         for (i = 0; i < tagsNumber; i += 1) {
28490             this.parseExifTag(
28491                 dataView,
28492                 tiffOffset,
28493                 dirOffset + 2 + 12 * i, // tag offset
28494                 littleEndian
28495             );
28496         }
28497         // Return the offset to the next directory:
28498         return dataView.getUint32(dirEndOffset, littleEndian);
28499     },
28500     
28501     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28502     {
28503         var tag = dataView.getUint16(offset, littleEndian);
28504         
28505         this.exif[tag] = this.getExifValue(
28506             dataView,
28507             tiffOffset,
28508             offset,
28509             dataView.getUint16(offset + 2, littleEndian), // tag type
28510             dataView.getUint32(offset + 4, littleEndian), // tag length
28511             littleEndian
28512         );
28513     },
28514     
28515     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28516     {
28517         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28518             tagSize,
28519             dataOffset,
28520             values,
28521             i,
28522             str,
28523             c;
28524     
28525         if (!tagType) {
28526             Roo.log('Invalid Exif data: Invalid tag type.');
28527             return;
28528         }
28529         
28530         tagSize = tagType.size * length;
28531         // Determine if the value is contained in the dataOffset bytes,
28532         // or if the value at the dataOffset is a pointer to the actual data:
28533         dataOffset = tagSize > 4 ?
28534                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28535         if (dataOffset + tagSize > dataView.byteLength) {
28536             Roo.log('Invalid Exif data: Invalid data offset.');
28537             return;
28538         }
28539         if (length === 1) {
28540             return tagType.getValue(dataView, dataOffset, littleEndian);
28541         }
28542         values = [];
28543         for (i = 0; i < length; i += 1) {
28544             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28545         }
28546         
28547         if (tagType.ascii) {
28548             str = '';
28549             // Concatenate the chars:
28550             for (i = 0; i < values.length; i += 1) {
28551                 c = values[i];
28552                 // Ignore the terminating NULL byte(s):
28553                 if (c === '\u0000') {
28554                     break;
28555                 }
28556                 str += c;
28557             }
28558             return str;
28559         }
28560         return values;
28561     }
28562     
28563 });
28564
28565 Roo.apply(Roo.bootstrap.UploadCropbox, {
28566     tags : {
28567         'Orientation': 0x0112
28568     },
28569     
28570     Orientation: {
28571             1: 0, //'top-left',
28572 //            2: 'top-right',
28573             3: 180, //'bottom-right',
28574 //            4: 'bottom-left',
28575 //            5: 'left-top',
28576             6: 90, //'right-top',
28577 //            7: 'right-bottom',
28578             8: 270 //'left-bottom'
28579     },
28580     
28581     exifTagTypes : {
28582         // byte, 8-bit unsigned int:
28583         1: {
28584             getValue: function (dataView, dataOffset) {
28585                 return dataView.getUint8(dataOffset);
28586             },
28587             size: 1
28588         },
28589         // ascii, 8-bit byte:
28590         2: {
28591             getValue: function (dataView, dataOffset) {
28592                 return String.fromCharCode(dataView.getUint8(dataOffset));
28593             },
28594             size: 1,
28595             ascii: true
28596         },
28597         // short, 16 bit int:
28598         3: {
28599             getValue: function (dataView, dataOffset, littleEndian) {
28600                 return dataView.getUint16(dataOffset, littleEndian);
28601             },
28602             size: 2
28603         },
28604         // long, 32 bit int:
28605         4: {
28606             getValue: function (dataView, dataOffset, littleEndian) {
28607                 return dataView.getUint32(dataOffset, littleEndian);
28608             },
28609             size: 4
28610         },
28611         // rational = two long values, first is numerator, second is denominator:
28612         5: {
28613             getValue: function (dataView, dataOffset, littleEndian) {
28614                 return dataView.getUint32(dataOffset, littleEndian) /
28615                     dataView.getUint32(dataOffset + 4, littleEndian);
28616             },
28617             size: 8
28618         },
28619         // slong, 32 bit signed int:
28620         9: {
28621             getValue: function (dataView, dataOffset, littleEndian) {
28622                 return dataView.getInt32(dataOffset, littleEndian);
28623             },
28624             size: 4
28625         },
28626         // srational, two slongs, first is numerator, second is denominator:
28627         10: {
28628             getValue: function (dataView, dataOffset, littleEndian) {
28629                 return dataView.getInt32(dataOffset, littleEndian) /
28630                     dataView.getInt32(dataOffset + 4, littleEndian);
28631             },
28632             size: 8
28633         }
28634     },
28635     
28636     footer : {
28637         STANDARD : [
28638             {
28639                 tag : 'div',
28640                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28641                 action : 'rotate-left',
28642                 cn : [
28643                     {
28644                         tag : 'button',
28645                         cls : 'btn btn-default',
28646                         html : '<i class="fa fa-undo"></i>'
28647                     }
28648                 ]
28649             },
28650             {
28651                 tag : 'div',
28652                 cls : 'btn-group roo-upload-cropbox-picture',
28653                 action : 'picture',
28654                 cn : [
28655                     {
28656                         tag : 'button',
28657                         cls : 'btn btn-default',
28658                         html : '<i class="fa fa-picture-o"></i>'
28659                     }
28660                 ]
28661             },
28662             {
28663                 tag : 'div',
28664                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28665                 action : 'rotate-right',
28666                 cn : [
28667                     {
28668                         tag : 'button',
28669                         cls : 'btn btn-default',
28670                         html : '<i class="fa fa-repeat"></i>'
28671                     }
28672                 ]
28673             }
28674         ],
28675         DOCUMENT : [
28676             {
28677                 tag : 'div',
28678                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28679                 action : 'rotate-left',
28680                 cn : [
28681                     {
28682                         tag : 'button',
28683                         cls : 'btn btn-default',
28684                         html : '<i class="fa fa-undo"></i>'
28685                     }
28686                 ]
28687             },
28688             {
28689                 tag : 'div',
28690                 cls : 'btn-group roo-upload-cropbox-download',
28691                 action : 'download',
28692                 cn : [
28693                     {
28694                         tag : 'button',
28695                         cls : 'btn btn-default',
28696                         html : '<i class="fa fa-download"></i>'
28697                     }
28698                 ]
28699             },
28700             {
28701                 tag : 'div',
28702                 cls : 'btn-group roo-upload-cropbox-crop',
28703                 action : 'crop',
28704                 cn : [
28705                     {
28706                         tag : 'button',
28707                         cls : 'btn btn-default',
28708                         html : '<i class="fa fa-crop"></i>'
28709                     }
28710                 ]
28711             },
28712             {
28713                 tag : 'div',
28714                 cls : 'btn-group roo-upload-cropbox-trash',
28715                 action : 'trash',
28716                 cn : [
28717                     {
28718                         tag : 'button',
28719                         cls : 'btn btn-default',
28720                         html : '<i class="fa fa-trash"></i>'
28721                     }
28722                 ]
28723             },
28724             {
28725                 tag : 'div',
28726                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28727                 action : 'rotate-right',
28728                 cn : [
28729                     {
28730                         tag : 'button',
28731                         cls : 'btn btn-default',
28732                         html : '<i class="fa fa-repeat"></i>'
28733                     }
28734                 ]
28735             }
28736         ],
28737         ROTATOR : [
28738             {
28739                 tag : 'div',
28740                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28741                 action : 'rotate-left',
28742                 cn : [
28743                     {
28744                         tag : 'button',
28745                         cls : 'btn btn-default',
28746                         html : '<i class="fa fa-undo"></i>'
28747                     }
28748                 ]
28749             },
28750             {
28751                 tag : 'div',
28752                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28753                 action : 'rotate-right',
28754                 cn : [
28755                     {
28756                         tag : 'button',
28757                         cls : 'btn btn-default',
28758                         html : '<i class="fa fa-repeat"></i>'
28759                     }
28760                 ]
28761             }
28762         ]
28763     }
28764 });
28765
28766 /*
28767 * Licence: LGPL
28768 */
28769
28770 /**
28771  * @class Roo.bootstrap.DocumentManager
28772  * @extends Roo.bootstrap.Component
28773  * Bootstrap DocumentManager class
28774  * @cfg {String} paramName default 'imageUpload'
28775  * @cfg {String} toolTipName default 'filename'
28776  * @cfg {String} method default POST
28777  * @cfg {String} url action url
28778  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28779  * @cfg {Boolean} multiple multiple upload default true
28780  * @cfg {Number} thumbSize default 300
28781  * @cfg {String} fieldLabel
28782  * @cfg {Number} labelWidth default 4
28783  * @cfg {String} labelAlign (left|top) default left
28784  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28785 * @cfg {Number} labellg set the width of label (1-12)
28786  * @cfg {Number} labelmd set the width of label (1-12)
28787  * @cfg {Number} labelsm set the width of label (1-12)
28788  * @cfg {Number} labelxs set the width of label (1-12)
28789  * 
28790  * @constructor
28791  * Create a new DocumentManager
28792  * @param {Object} config The config object
28793  */
28794
28795 Roo.bootstrap.DocumentManager = function(config){
28796     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28797     
28798     this.files = [];
28799     this.delegates = [];
28800     
28801     this.addEvents({
28802         /**
28803          * @event initial
28804          * Fire when initial the DocumentManager
28805          * @param {Roo.bootstrap.DocumentManager} this
28806          */
28807         "initial" : true,
28808         /**
28809          * @event inspect
28810          * inspect selected file
28811          * @param {Roo.bootstrap.DocumentManager} this
28812          * @param {File} file
28813          */
28814         "inspect" : true,
28815         /**
28816          * @event exception
28817          * Fire when xhr load exception
28818          * @param {Roo.bootstrap.DocumentManager} this
28819          * @param {XMLHttpRequest} xhr
28820          */
28821         "exception" : true,
28822         /**
28823          * @event afterupload
28824          * Fire when xhr load exception
28825          * @param {Roo.bootstrap.DocumentManager} this
28826          * @param {XMLHttpRequest} xhr
28827          */
28828         "afterupload" : true,
28829         /**
28830          * @event prepare
28831          * prepare the form data
28832          * @param {Roo.bootstrap.DocumentManager} this
28833          * @param {Object} formData
28834          */
28835         "prepare" : true,
28836         /**
28837          * @event remove
28838          * Fire when remove the file
28839          * @param {Roo.bootstrap.DocumentManager} this
28840          * @param {Object} file
28841          */
28842         "remove" : true,
28843         /**
28844          * @event refresh
28845          * Fire after refresh the file
28846          * @param {Roo.bootstrap.DocumentManager} this
28847          */
28848         "refresh" : true,
28849         /**
28850          * @event click
28851          * Fire after click the image
28852          * @param {Roo.bootstrap.DocumentManager} this
28853          * @param {Object} file
28854          */
28855         "click" : true,
28856         /**
28857          * @event edit
28858          * Fire when upload a image and editable set to true
28859          * @param {Roo.bootstrap.DocumentManager} this
28860          * @param {Object} file
28861          */
28862         "edit" : true,
28863         /**
28864          * @event beforeselectfile
28865          * Fire before select file
28866          * @param {Roo.bootstrap.DocumentManager} this
28867          */
28868         "beforeselectfile" : true,
28869         /**
28870          * @event process
28871          * Fire before process file
28872          * @param {Roo.bootstrap.DocumentManager} this
28873          * @param {Object} file
28874          */
28875         "process" : true,
28876         /**
28877          * @event previewrendered
28878          * Fire when preview rendered
28879          * @param {Roo.bootstrap.DocumentManager} this
28880          * @param {Object} file
28881          */
28882         "previewrendered" : true,
28883         /**
28884          */
28885         "previewResize" : true
28886         
28887     });
28888 };
28889
28890 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28891     
28892     boxes : 0,
28893     inputName : '',
28894     thumbSize : 300,
28895     multiple : true,
28896     files : false,
28897     method : 'POST',
28898     url : '',
28899     paramName : 'imageUpload',
28900     toolTipName : 'filename',
28901     fieldLabel : '',
28902     labelWidth : 4,
28903     labelAlign : 'left',
28904     editable : true,
28905     delegates : false,
28906     xhr : false, 
28907     
28908     labellg : 0,
28909     labelmd : 0,
28910     labelsm : 0,
28911     labelxs : 0,
28912     
28913     getAutoCreate : function()
28914     {   
28915         var managerWidget = {
28916             tag : 'div',
28917             cls : 'roo-document-manager',
28918             cn : [
28919                 {
28920                     tag : 'input',
28921                     cls : 'roo-document-manager-selector',
28922                     type : 'file'
28923                 },
28924                 {
28925                     tag : 'div',
28926                     cls : 'roo-document-manager-uploader',
28927                     cn : [
28928                         {
28929                             tag : 'div',
28930                             cls : 'roo-document-manager-upload-btn',
28931                             html : '<i class="fa fa-plus"></i>'
28932                         }
28933                     ]
28934                     
28935                 }
28936             ]
28937         };
28938         
28939         var content = [
28940             {
28941                 tag : 'div',
28942                 cls : 'column col-md-12',
28943                 cn : managerWidget
28944             }
28945         ];
28946         
28947         if(this.fieldLabel.length){
28948             
28949             content = [
28950                 {
28951                     tag : 'div',
28952                     cls : 'column col-md-12',
28953                     html : this.fieldLabel
28954                 },
28955                 {
28956                     tag : 'div',
28957                     cls : 'column col-md-12',
28958                     cn : managerWidget
28959                 }
28960             ];
28961
28962             if(this.labelAlign == 'left'){
28963                 content = [
28964                     {
28965                         tag : 'div',
28966                         cls : 'column',
28967                         html : this.fieldLabel
28968                     },
28969                     {
28970                         tag : 'div',
28971                         cls : 'column',
28972                         cn : managerWidget
28973                     }
28974                 ];
28975                 
28976                 if(this.labelWidth > 12){
28977                     content[0].style = "width: " + this.labelWidth + 'px';
28978                 }
28979
28980                 if(this.labelWidth < 13 && this.labelmd == 0){
28981                     this.labelmd = this.labelWidth;
28982                 }
28983
28984                 if(this.labellg > 0){
28985                     content[0].cls += ' col-lg-' + this.labellg;
28986                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28987                 }
28988
28989                 if(this.labelmd > 0){
28990                     content[0].cls += ' col-md-' + this.labelmd;
28991                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28992                 }
28993
28994                 if(this.labelsm > 0){
28995                     content[0].cls += ' col-sm-' + this.labelsm;
28996                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28997                 }
28998
28999                 if(this.labelxs > 0){
29000                     content[0].cls += ' col-xs-' + this.labelxs;
29001                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29002                 }
29003                 
29004             }
29005         }
29006         
29007         var cfg = {
29008             tag : 'div',
29009             cls : 'row clearfix',
29010             cn : content
29011         };
29012         
29013         return cfg;
29014         
29015     },
29016     
29017     initEvents : function()
29018     {
29019         this.managerEl = this.el.select('.roo-document-manager', true).first();
29020         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29021         
29022         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29023         this.selectorEl.hide();
29024         
29025         if(this.multiple){
29026             this.selectorEl.attr('multiple', 'multiple');
29027         }
29028         
29029         this.selectorEl.on('change', this.onFileSelected, this);
29030         
29031         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29032         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29033         
29034         this.uploader.on('click', this.onUploaderClick, this);
29035         
29036         this.renderProgressDialog();
29037         
29038         var _this = this;
29039         
29040         window.addEventListener("resize", function() { _this.refresh(); } );
29041         
29042         this.fireEvent('initial', this);
29043     },
29044     
29045     renderProgressDialog : function()
29046     {
29047         var _this = this;
29048         
29049         this.progressDialog = new Roo.bootstrap.Modal({
29050             cls : 'roo-document-manager-progress-dialog',
29051             allow_close : false,
29052             title : '',
29053             buttons : [
29054                 {
29055                     name  :'cancel',
29056                     weight : 'danger',
29057                     html : 'Cancel'
29058                 }
29059             ], 
29060             listeners : { 
29061                 btnclick : function() {
29062                     _this.uploadCancel();
29063                     this.hide();
29064                 }
29065             }
29066         });
29067          
29068         this.progressDialog.render(Roo.get(document.body));
29069          
29070         this.progress = new Roo.bootstrap.Progress({
29071             cls : 'roo-document-manager-progress',
29072             active : true,
29073             striped : true
29074         });
29075         
29076         this.progress.render(this.progressDialog.getChildContainer());
29077         
29078         this.progressBar = new Roo.bootstrap.ProgressBar({
29079             cls : 'roo-document-manager-progress-bar',
29080             aria_valuenow : 0,
29081             aria_valuemin : 0,
29082             aria_valuemax : 12,
29083             panel : 'success'
29084         });
29085         
29086         this.progressBar.render(this.progress.getChildContainer());
29087     },
29088     
29089     onUploaderClick : function(e)
29090     {
29091         e.preventDefault();
29092      
29093         if(this.fireEvent('beforeselectfile', this) != false){
29094             this.selectorEl.dom.click();
29095         }
29096         
29097     },
29098     
29099     onFileSelected : function(e)
29100     {
29101         e.preventDefault();
29102         
29103         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29104             return;
29105         }
29106         
29107         Roo.each(this.selectorEl.dom.files, function(file){
29108             if(this.fireEvent('inspect', this, file) != false){
29109                 this.files.push(file);
29110             }
29111         }, this);
29112         
29113         this.queue();
29114         
29115     },
29116     
29117     queue : function()
29118     {
29119         this.selectorEl.dom.value = '';
29120         
29121         if(!this.files || !this.files.length){
29122             return;
29123         }
29124         
29125         if(this.boxes > 0 && this.files.length > this.boxes){
29126             this.files = this.files.slice(0, this.boxes);
29127         }
29128         
29129         this.uploader.show();
29130         
29131         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29132             this.uploader.hide();
29133         }
29134         
29135         var _this = this;
29136         
29137         var files = [];
29138         
29139         var docs = [];
29140         
29141         Roo.each(this.files, function(file){
29142             
29143             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29144                 var f = this.renderPreview(file);
29145                 files.push(f);
29146                 return;
29147             }
29148             
29149             if(file.type.indexOf('image') != -1){
29150                 this.delegates.push(
29151                     (function(){
29152                         _this.process(file);
29153                     }).createDelegate(this)
29154                 );
29155         
29156                 return;
29157             }
29158             
29159             docs.push(
29160                 (function(){
29161                     _this.process(file);
29162                 }).createDelegate(this)
29163             );
29164             
29165         }, this);
29166         
29167         this.files = files;
29168         
29169         this.delegates = this.delegates.concat(docs);
29170         
29171         if(!this.delegates.length){
29172             this.refresh();
29173             return;
29174         }
29175         
29176         this.progressBar.aria_valuemax = this.delegates.length;
29177         
29178         this.arrange();
29179         
29180         return;
29181     },
29182     
29183     arrange : function()
29184     {
29185         if(!this.delegates.length){
29186             this.progressDialog.hide();
29187             this.refresh();
29188             return;
29189         }
29190         
29191         var delegate = this.delegates.shift();
29192         
29193         this.progressDialog.show();
29194         
29195         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29196         
29197         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29198         
29199         delegate();
29200     },
29201     
29202     refresh : function()
29203     {
29204         this.uploader.show();
29205         
29206         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29207             this.uploader.hide();
29208         }
29209         
29210         Roo.isTouch ? this.closable(false) : this.closable(true);
29211         
29212         this.fireEvent('refresh', this);
29213     },
29214     
29215     onRemove : function(e, el, o)
29216     {
29217         e.preventDefault();
29218         
29219         this.fireEvent('remove', this, o);
29220         
29221     },
29222     
29223     remove : function(o)
29224     {
29225         var files = [];
29226         
29227         Roo.each(this.files, function(file){
29228             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29229                 files.push(file);
29230                 return;
29231             }
29232
29233             o.target.remove();
29234
29235         }, this);
29236         
29237         this.files = files;
29238         
29239         this.refresh();
29240     },
29241     
29242     clear : function()
29243     {
29244         Roo.each(this.files, function(file){
29245             if(!file.target){
29246                 return;
29247             }
29248             
29249             file.target.remove();
29250
29251         }, this);
29252         
29253         this.files = [];
29254         
29255         this.refresh();
29256     },
29257     
29258     onClick : function(e, el, o)
29259     {
29260         e.preventDefault();
29261         
29262         this.fireEvent('click', this, o);
29263         
29264     },
29265     
29266     closable : function(closable)
29267     {
29268         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29269             
29270             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29271             
29272             if(closable){
29273                 el.show();
29274                 return;
29275             }
29276             
29277             el.hide();
29278             
29279         }, this);
29280     },
29281     
29282     xhrOnLoad : function(xhr)
29283     {
29284         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29285             el.remove();
29286         }, this);
29287         
29288         if (xhr.readyState !== 4) {
29289             this.arrange();
29290             this.fireEvent('exception', this, xhr);
29291             return;
29292         }
29293
29294         var response = Roo.decode(xhr.responseText);
29295         
29296         if(!response.success){
29297             this.arrange();
29298             this.fireEvent('exception', this, xhr);
29299             return;
29300         }
29301         
29302         var file = this.renderPreview(response.data);
29303         
29304         this.files.push(file);
29305         
29306         this.arrange();
29307         
29308         this.fireEvent('afterupload', this, xhr);
29309         
29310     },
29311     
29312     xhrOnError : function(xhr)
29313     {
29314         Roo.log('xhr on error');
29315         
29316         var response = Roo.decode(xhr.responseText);
29317           
29318         Roo.log(response);
29319         
29320         this.arrange();
29321     },
29322     
29323     process : function(file)
29324     {
29325         if(this.fireEvent('process', this, file) !== false){
29326             if(this.editable && file.type.indexOf('image') != -1){
29327                 this.fireEvent('edit', this, file);
29328                 return;
29329             }
29330
29331             this.uploadStart(file, false);
29332
29333             return;
29334         }
29335         
29336     },
29337     
29338     uploadStart : function(file, crop)
29339     {
29340         this.xhr = new XMLHttpRequest();
29341         
29342         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29343             this.arrange();
29344             return;
29345         }
29346         
29347         file.xhr = this.xhr;
29348             
29349         this.managerEl.createChild({
29350             tag : 'div',
29351             cls : 'roo-document-manager-loading',
29352             cn : [
29353                 {
29354                     tag : 'div',
29355                     tooltip : file.name,
29356                     cls : 'roo-document-manager-thumb',
29357                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29358                 }
29359             ]
29360
29361         });
29362
29363         this.xhr.open(this.method, this.url, true);
29364         
29365         var headers = {
29366             "Accept": "application/json",
29367             "Cache-Control": "no-cache",
29368             "X-Requested-With": "XMLHttpRequest"
29369         };
29370         
29371         for (var headerName in headers) {
29372             var headerValue = headers[headerName];
29373             if (headerValue) {
29374                 this.xhr.setRequestHeader(headerName, headerValue);
29375             }
29376         }
29377         
29378         var _this = this;
29379         
29380         this.xhr.onload = function()
29381         {
29382             _this.xhrOnLoad(_this.xhr);
29383         }
29384         
29385         this.xhr.onerror = function()
29386         {
29387             _this.xhrOnError(_this.xhr);
29388         }
29389         
29390         var formData = new FormData();
29391
29392         formData.append('returnHTML', 'NO');
29393         
29394         if(crop){
29395             formData.append('crop', crop);
29396         }
29397         
29398         formData.append(this.paramName, file, file.name);
29399         
29400         var options = {
29401             file : file, 
29402             manually : false
29403         };
29404         
29405         if(this.fireEvent('prepare', this, formData, options) != false){
29406             
29407             if(options.manually){
29408                 return;
29409             }
29410             
29411             this.xhr.send(formData);
29412             return;
29413         };
29414         
29415         this.uploadCancel();
29416     },
29417     
29418     uploadCancel : function()
29419     {
29420         if (this.xhr) {
29421             this.xhr.abort();
29422         }
29423         
29424         this.delegates = [];
29425         
29426         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29427             el.remove();
29428         }, this);
29429         
29430         this.arrange();
29431     },
29432     
29433     renderPreview : function(file)
29434     {
29435         if(typeof(file.target) != 'undefined' && file.target){
29436             return file;
29437         }
29438         
29439         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29440         
29441         var previewEl = this.managerEl.createChild({
29442             tag : 'div',
29443             cls : 'roo-document-manager-preview',
29444             cn : [
29445                 {
29446                     tag : 'div',
29447                     tooltip : file[this.toolTipName],
29448                     cls : 'roo-document-manager-thumb',
29449                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29450                 },
29451                 {
29452                     tag : 'button',
29453                     cls : 'close',
29454                     html : '<i class="fa fa-times-circle"></i>'
29455                 }
29456             ]
29457         });
29458
29459         var close = previewEl.select('button.close', true).first();
29460
29461         close.on('click', this.onRemove, this, file);
29462
29463         file.target = previewEl;
29464
29465         var image = previewEl.select('img', true).first();
29466         
29467         var _this = this;
29468         
29469         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29470         
29471         image.on('click', this.onClick, this, file);
29472         
29473         this.fireEvent('previewrendered', this, file);
29474         
29475         return file;
29476         
29477     },
29478     
29479     onPreviewLoad : function(file, image)
29480     {
29481         if(typeof(file.target) == 'undefined' || !file.target){
29482             return;
29483         }
29484         
29485         var width = image.dom.naturalWidth || image.dom.width;
29486         var height = image.dom.naturalHeight || image.dom.height;
29487         
29488         if(!this.previewResize) {
29489             return;
29490         }
29491         
29492         if(width > height){
29493             file.target.addClass('wide');
29494             return;
29495         }
29496         
29497         file.target.addClass('tall');
29498         return;
29499         
29500     },
29501     
29502     uploadFromSource : function(file, crop)
29503     {
29504         this.xhr = new XMLHttpRequest();
29505         
29506         this.managerEl.createChild({
29507             tag : 'div',
29508             cls : 'roo-document-manager-loading',
29509             cn : [
29510                 {
29511                     tag : 'div',
29512                     tooltip : file.name,
29513                     cls : 'roo-document-manager-thumb',
29514                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29515                 }
29516             ]
29517
29518         });
29519
29520         this.xhr.open(this.method, this.url, true);
29521         
29522         var headers = {
29523             "Accept": "application/json",
29524             "Cache-Control": "no-cache",
29525             "X-Requested-With": "XMLHttpRequest"
29526         };
29527         
29528         for (var headerName in headers) {
29529             var headerValue = headers[headerName];
29530             if (headerValue) {
29531                 this.xhr.setRequestHeader(headerName, headerValue);
29532             }
29533         }
29534         
29535         var _this = this;
29536         
29537         this.xhr.onload = function()
29538         {
29539             _this.xhrOnLoad(_this.xhr);
29540         }
29541         
29542         this.xhr.onerror = function()
29543         {
29544             _this.xhrOnError(_this.xhr);
29545         }
29546         
29547         var formData = new FormData();
29548
29549         formData.append('returnHTML', 'NO');
29550         
29551         formData.append('crop', crop);
29552         
29553         if(typeof(file.filename) != 'undefined'){
29554             formData.append('filename', file.filename);
29555         }
29556         
29557         if(typeof(file.mimetype) != 'undefined'){
29558             formData.append('mimetype', file.mimetype);
29559         }
29560         
29561         Roo.log(formData);
29562         
29563         if(this.fireEvent('prepare', this, formData) != false){
29564             this.xhr.send(formData);
29565         };
29566     }
29567 });
29568
29569 /*
29570 * Licence: LGPL
29571 */
29572
29573 /**
29574  * @class Roo.bootstrap.DocumentViewer
29575  * @extends Roo.bootstrap.Component
29576  * Bootstrap DocumentViewer class
29577  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29578  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29579  * 
29580  * @constructor
29581  * Create a new DocumentViewer
29582  * @param {Object} config The config object
29583  */
29584
29585 Roo.bootstrap.DocumentViewer = function(config){
29586     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29587     
29588     this.addEvents({
29589         /**
29590          * @event initial
29591          * Fire after initEvent
29592          * @param {Roo.bootstrap.DocumentViewer} this
29593          */
29594         "initial" : true,
29595         /**
29596          * @event click
29597          * Fire after click
29598          * @param {Roo.bootstrap.DocumentViewer} this
29599          */
29600         "click" : true,
29601         /**
29602          * @event download
29603          * Fire after download button
29604          * @param {Roo.bootstrap.DocumentViewer} this
29605          */
29606         "download" : true,
29607         /**
29608          * @event trash
29609          * Fire after trash button
29610          * @param {Roo.bootstrap.DocumentViewer} this
29611          */
29612         "trash" : true
29613         
29614     });
29615 };
29616
29617 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29618     
29619     showDownload : true,
29620     
29621     showTrash : true,
29622     
29623     getAutoCreate : function()
29624     {
29625         var cfg = {
29626             tag : 'div',
29627             cls : 'roo-document-viewer',
29628             cn : [
29629                 {
29630                     tag : 'div',
29631                     cls : 'roo-document-viewer-body',
29632                     cn : [
29633                         {
29634                             tag : 'div',
29635                             cls : 'roo-document-viewer-thumb',
29636                             cn : [
29637                                 {
29638                                     tag : 'img',
29639                                     cls : 'roo-document-viewer-image'
29640                                 }
29641                             ]
29642                         }
29643                     ]
29644                 },
29645                 {
29646                     tag : 'div',
29647                     cls : 'roo-document-viewer-footer',
29648                     cn : {
29649                         tag : 'div',
29650                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29651                         cn : [
29652                             {
29653                                 tag : 'div',
29654                                 cls : 'btn-group roo-document-viewer-download',
29655                                 cn : [
29656                                     {
29657                                         tag : 'button',
29658                                         cls : 'btn btn-default',
29659                                         html : '<i class="fa fa-download"></i>'
29660                                     }
29661                                 ]
29662                             },
29663                             {
29664                                 tag : 'div',
29665                                 cls : 'btn-group roo-document-viewer-trash',
29666                                 cn : [
29667                                     {
29668                                         tag : 'button',
29669                                         cls : 'btn btn-default',
29670                                         html : '<i class="fa fa-trash"></i>'
29671                                     }
29672                                 ]
29673                             }
29674                         ]
29675                     }
29676                 }
29677             ]
29678         };
29679         
29680         return cfg;
29681     },
29682     
29683     initEvents : function()
29684     {
29685         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29686         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29687         
29688         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29689         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29690         
29691         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29692         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29693         
29694         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29695         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29696         
29697         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29698         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29699         
29700         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29701         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29702         
29703         this.bodyEl.on('click', this.onClick, this);
29704         this.downloadBtn.on('click', this.onDownload, this);
29705         this.trashBtn.on('click', this.onTrash, this);
29706         
29707         this.downloadBtn.hide();
29708         this.trashBtn.hide();
29709         
29710         if(this.showDownload){
29711             this.downloadBtn.show();
29712         }
29713         
29714         if(this.showTrash){
29715             this.trashBtn.show();
29716         }
29717         
29718         if(!this.showDownload && !this.showTrash) {
29719             this.footerEl.hide();
29720         }
29721         
29722     },
29723     
29724     initial : function()
29725     {
29726         this.fireEvent('initial', this);
29727         
29728     },
29729     
29730     onClick : function(e)
29731     {
29732         e.preventDefault();
29733         
29734         this.fireEvent('click', this);
29735     },
29736     
29737     onDownload : function(e)
29738     {
29739         e.preventDefault();
29740         
29741         this.fireEvent('download', this);
29742     },
29743     
29744     onTrash : function(e)
29745     {
29746         e.preventDefault();
29747         
29748         this.fireEvent('trash', this);
29749     }
29750     
29751 });
29752 /*
29753  * - LGPL
29754  *
29755  * nav progress bar
29756  * 
29757  */
29758
29759 /**
29760  * @class Roo.bootstrap.NavProgressBar
29761  * @extends Roo.bootstrap.Component
29762  * Bootstrap NavProgressBar class
29763  * 
29764  * @constructor
29765  * Create a new nav progress bar
29766  * @param {Object} config The config object
29767  */
29768
29769 Roo.bootstrap.NavProgressBar = function(config){
29770     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29771
29772     this.bullets = this.bullets || [];
29773    
29774 //    Roo.bootstrap.NavProgressBar.register(this);
29775      this.addEvents({
29776         /**
29777              * @event changed
29778              * Fires when the active item changes
29779              * @param {Roo.bootstrap.NavProgressBar} this
29780              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29781              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29782          */
29783         'changed': true
29784      });
29785     
29786 };
29787
29788 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29789     
29790     bullets : [],
29791     barItems : [],
29792     
29793     getAutoCreate : function()
29794     {
29795         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29796         
29797         cfg = {
29798             tag : 'div',
29799             cls : 'roo-navigation-bar-group',
29800             cn : [
29801                 {
29802                     tag : 'div',
29803                     cls : 'roo-navigation-top-bar'
29804                 },
29805                 {
29806                     tag : 'div',
29807                     cls : 'roo-navigation-bullets-bar',
29808                     cn : [
29809                         {
29810                             tag : 'ul',
29811                             cls : 'roo-navigation-bar'
29812                         }
29813                     ]
29814                 },
29815                 
29816                 {
29817                     tag : 'div',
29818                     cls : 'roo-navigation-bottom-bar'
29819                 }
29820             ]
29821             
29822         };
29823         
29824         return cfg;
29825         
29826     },
29827     
29828     initEvents: function() 
29829     {
29830         
29831     },
29832     
29833     onRender : function(ct, position) 
29834     {
29835         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29836         
29837         if(this.bullets.length){
29838             Roo.each(this.bullets, function(b){
29839                this.addItem(b);
29840             }, this);
29841         }
29842         
29843         this.format();
29844         
29845     },
29846     
29847     addItem : function(cfg)
29848     {
29849         var item = new Roo.bootstrap.NavProgressItem(cfg);
29850         
29851         item.parentId = this.id;
29852         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29853         
29854         if(cfg.html){
29855             var top = new Roo.bootstrap.Element({
29856                 tag : 'div',
29857                 cls : 'roo-navigation-bar-text'
29858             });
29859             
29860             var bottom = new Roo.bootstrap.Element({
29861                 tag : 'div',
29862                 cls : 'roo-navigation-bar-text'
29863             });
29864             
29865             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29866             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29867             
29868             var topText = new Roo.bootstrap.Element({
29869                 tag : 'span',
29870                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29871             });
29872             
29873             var bottomText = new Roo.bootstrap.Element({
29874                 tag : 'span',
29875                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29876             });
29877             
29878             topText.onRender(top.el, null);
29879             bottomText.onRender(bottom.el, null);
29880             
29881             item.topEl = top;
29882             item.bottomEl = bottom;
29883         }
29884         
29885         this.barItems.push(item);
29886         
29887         return item;
29888     },
29889     
29890     getActive : function()
29891     {
29892         var active = false;
29893         
29894         Roo.each(this.barItems, function(v){
29895             
29896             if (!v.isActive()) {
29897                 return;
29898             }
29899             
29900             active = v;
29901             return false;
29902             
29903         });
29904         
29905         return active;
29906     },
29907     
29908     setActiveItem : function(item)
29909     {
29910         var prev = false;
29911         
29912         Roo.each(this.barItems, function(v){
29913             if (v.rid == item.rid) {
29914                 return ;
29915             }
29916             
29917             if (v.isActive()) {
29918                 v.setActive(false);
29919                 prev = v;
29920             }
29921         });
29922
29923         item.setActive(true);
29924         
29925         this.fireEvent('changed', this, item, prev);
29926     },
29927     
29928     getBarItem: function(rid)
29929     {
29930         var ret = false;
29931         
29932         Roo.each(this.barItems, function(e) {
29933             if (e.rid != rid) {
29934                 return;
29935             }
29936             
29937             ret =  e;
29938             return false;
29939         });
29940         
29941         return ret;
29942     },
29943     
29944     indexOfItem : function(item)
29945     {
29946         var index = false;
29947         
29948         Roo.each(this.barItems, function(v, i){
29949             
29950             if (v.rid != item.rid) {
29951                 return;
29952             }
29953             
29954             index = i;
29955             return false
29956         });
29957         
29958         return index;
29959     },
29960     
29961     setActiveNext : function()
29962     {
29963         var i = this.indexOfItem(this.getActive());
29964         
29965         if (i > this.barItems.length) {
29966             return;
29967         }
29968         
29969         this.setActiveItem(this.barItems[i+1]);
29970     },
29971     
29972     setActivePrev : function()
29973     {
29974         var i = this.indexOfItem(this.getActive());
29975         
29976         if (i  < 1) {
29977             return;
29978         }
29979         
29980         this.setActiveItem(this.barItems[i-1]);
29981     },
29982     
29983     format : function()
29984     {
29985         if(!this.barItems.length){
29986             return;
29987         }
29988      
29989         var width = 100 / this.barItems.length;
29990         
29991         Roo.each(this.barItems, function(i){
29992             i.el.setStyle('width', width + '%');
29993             i.topEl.el.setStyle('width', width + '%');
29994             i.bottomEl.el.setStyle('width', width + '%');
29995         }, this);
29996         
29997     }
29998     
29999 });
30000 /*
30001  * - LGPL
30002  *
30003  * Nav Progress Item
30004  * 
30005  */
30006
30007 /**
30008  * @class Roo.bootstrap.NavProgressItem
30009  * @extends Roo.bootstrap.Component
30010  * Bootstrap NavProgressItem class
30011  * @cfg {String} rid the reference id
30012  * @cfg {Boolean} active (true|false) Is item active default false
30013  * @cfg {Boolean} disabled (true|false) Is item active default false
30014  * @cfg {String} html
30015  * @cfg {String} position (top|bottom) text position default bottom
30016  * @cfg {String} icon show icon instead of number
30017  * 
30018  * @constructor
30019  * Create a new NavProgressItem
30020  * @param {Object} config The config object
30021  */
30022 Roo.bootstrap.NavProgressItem = function(config){
30023     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30024     this.addEvents({
30025         // raw events
30026         /**
30027          * @event click
30028          * The raw click event for the entire grid.
30029          * @param {Roo.bootstrap.NavProgressItem} this
30030          * @param {Roo.EventObject} e
30031          */
30032         "click" : true
30033     });
30034    
30035 };
30036
30037 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30038     
30039     rid : '',
30040     active : false,
30041     disabled : false,
30042     html : '',
30043     position : 'bottom',
30044     icon : false,
30045     
30046     getAutoCreate : function()
30047     {
30048         var iconCls = 'roo-navigation-bar-item-icon';
30049         
30050         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30051         
30052         var cfg = {
30053             tag: 'li',
30054             cls: 'roo-navigation-bar-item',
30055             cn : [
30056                 {
30057                     tag : 'i',
30058                     cls : iconCls
30059                 }
30060             ]
30061         };
30062         
30063         if(this.active){
30064             cfg.cls += ' active';
30065         }
30066         if(this.disabled){
30067             cfg.cls += ' disabled';
30068         }
30069         
30070         return cfg;
30071     },
30072     
30073     disable : function()
30074     {
30075         this.setDisabled(true);
30076     },
30077     
30078     enable : function()
30079     {
30080         this.setDisabled(false);
30081     },
30082     
30083     initEvents: function() 
30084     {
30085         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30086         
30087         this.iconEl.on('click', this.onClick, this);
30088     },
30089     
30090     onClick : function(e)
30091     {
30092         e.preventDefault();
30093         
30094         if(this.disabled){
30095             return;
30096         }
30097         
30098         if(this.fireEvent('click', this, e) === false){
30099             return;
30100         };
30101         
30102         this.parent().setActiveItem(this);
30103     },
30104     
30105     isActive: function () 
30106     {
30107         return this.active;
30108     },
30109     
30110     setActive : function(state)
30111     {
30112         if(this.active == state){
30113             return;
30114         }
30115         
30116         this.active = state;
30117         
30118         if (state) {
30119             this.el.addClass('active');
30120             return;
30121         }
30122         
30123         this.el.removeClass('active');
30124         
30125         return;
30126     },
30127     
30128     setDisabled : function(state)
30129     {
30130         if(this.disabled == state){
30131             return;
30132         }
30133         
30134         this.disabled = state;
30135         
30136         if (state) {
30137             this.el.addClass('disabled');
30138             return;
30139         }
30140         
30141         this.el.removeClass('disabled');
30142     },
30143     
30144     tooltipEl : function()
30145     {
30146         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30147     }
30148 });
30149  
30150
30151  /*
30152  * - LGPL
30153  *
30154  * FieldLabel
30155  * 
30156  */
30157
30158 /**
30159  * @class Roo.bootstrap.FieldLabel
30160  * @extends Roo.bootstrap.Component
30161  * Bootstrap FieldLabel class
30162  * @cfg {String} html contents of the element
30163  * @cfg {String} tag tag of the element default label
30164  * @cfg {String} cls class of the element
30165  * @cfg {String} target label target 
30166  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30167  * @cfg {String} invalidClass default "text-warning"
30168  * @cfg {String} validClass default "text-success"
30169  * @cfg {String} iconTooltip default "This field is required"
30170  * @cfg {String} indicatorpos (left|right) default left
30171  * 
30172  * @constructor
30173  * Create a new FieldLabel
30174  * @param {Object} config The config object
30175  */
30176
30177 Roo.bootstrap.FieldLabel = function(config){
30178     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30179     
30180     this.addEvents({
30181             /**
30182              * @event invalid
30183              * Fires after the field has been marked as invalid.
30184              * @param {Roo.form.FieldLabel} this
30185              * @param {String} msg The validation message
30186              */
30187             invalid : true,
30188             /**
30189              * @event valid
30190              * Fires after the field has been validated with no errors.
30191              * @param {Roo.form.FieldLabel} this
30192              */
30193             valid : true
30194         });
30195 };
30196
30197 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30198     
30199     tag: 'label',
30200     cls: '',
30201     html: '',
30202     target: '',
30203     allowBlank : true,
30204     invalidClass : 'has-warning',
30205     validClass : 'has-success',
30206     iconTooltip : 'This field is required',
30207     indicatorpos : 'left',
30208     
30209     getAutoCreate : function(){
30210         
30211         var cls = "";
30212         if (!this.allowBlank) {
30213             cls  = "visible";
30214         }
30215         
30216         var cfg = {
30217             tag : this.tag,
30218             cls : 'roo-bootstrap-field-label ' + this.cls,
30219             for : this.target,
30220             cn : [
30221                 {
30222                     tag : 'i',
30223                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30224                     tooltip : this.iconTooltip
30225                 },
30226                 {
30227                     tag : 'span',
30228                     html : this.html
30229                 }
30230             ] 
30231         };
30232         
30233         if(this.indicatorpos == 'right'){
30234             var cfg = {
30235                 tag : this.tag,
30236                 cls : 'roo-bootstrap-field-label ' + this.cls,
30237                 for : this.target,
30238                 cn : [
30239                     {
30240                         tag : 'span',
30241                         html : this.html
30242                     },
30243                     {
30244                         tag : 'i',
30245                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30246                         tooltip : this.iconTooltip
30247                     }
30248                 ] 
30249             };
30250         }
30251         
30252         return cfg;
30253     },
30254     
30255     initEvents: function() 
30256     {
30257         Roo.bootstrap.Element.superclass.initEvents.call(this);
30258         
30259         this.indicator = this.indicatorEl();
30260         
30261         if(this.indicator){
30262             this.indicator.removeClass('visible');
30263             this.indicator.addClass('invisible');
30264         }
30265         
30266         Roo.bootstrap.FieldLabel.register(this);
30267     },
30268     
30269     indicatorEl : function()
30270     {
30271         var indicator = this.el.select('i.roo-required-indicator',true).first();
30272         
30273         if(!indicator){
30274             return false;
30275         }
30276         
30277         return indicator;
30278         
30279     },
30280     
30281     /**
30282      * Mark this field as valid
30283      */
30284     markValid : function()
30285     {
30286         if(this.indicator){
30287             this.indicator.removeClass('visible');
30288             this.indicator.addClass('invisible');
30289         }
30290         
30291         this.el.removeClass(this.invalidClass);
30292         
30293         this.el.addClass(this.validClass);
30294         
30295         this.fireEvent('valid', this);
30296     },
30297     
30298     /**
30299      * Mark this field as invalid
30300      * @param {String} msg The validation message
30301      */
30302     markInvalid : function(msg)
30303     {
30304         if(this.indicator){
30305             this.indicator.removeClass('invisible');
30306             this.indicator.addClass('visible');
30307         }
30308         
30309         this.el.removeClass(this.validClass);
30310         
30311         this.el.addClass(this.invalidClass);
30312         
30313         this.fireEvent('invalid', this, msg);
30314     }
30315     
30316    
30317 });
30318
30319 Roo.apply(Roo.bootstrap.FieldLabel, {
30320     
30321     groups: {},
30322     
30323      /**
30324     * register a FieldLabel Group
30325     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30326     */
30327     register : function(label)
30328     {
30329         if(this.groups.hasOwnProperty(label.target)){
30330             return;
30331         }
30332      
30333         this.groups[label.target] = label;
30334         
30335     },
30336     /**
30337     * fetch a FieldLabel Group based on the target
30338     * @param {string} target
30339     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30340     */
30341     get: function(target) {
30342         if (typeof(this.groups[target]) == 'undefined') {
30343             return false;
30344         }
30345         
30346         return this.groups[target] ;
30347     }
30348 });
30349
30350  
30351
30352  /*
30353  * - LGPL
30354  *
30355  * page DateSplitField.
30356  * 
30357  */
30358
30359
30360 /**
30361  * @class Roo.bootstrap.DateSplitField
30362  * @extends Roo.bootstrap.Component
30363  * Bootstrap DateSplitField class
30364  * @cfg {string} fieldLabel - the label associated
30365  * @cfg {Number} labelWidth set the width of label (0-12)
30366  * @cfg {String} labelAlign (top|left)
30367  * @cfg {Boolean} dayAllowBlank (true|false) default false
30368  * @cfg {Boolean} monthAllowBlank (true|false) default false
30369  * @cfg {Boolean} yearAllowBlank (true|false) default false
30370  * @cfg {string} dayPlaceholder 
30371  * @cfg {string} monthPlaceholder
30372  * @cfg {string} yearPlaceholder
30373  * @cfg {string} dayFormat default 'd'
30374  * @cfg {string} monthFormat default 'm'
30375  * @cfg {string} yearFormat default 'Y'
30376  * @cfg {Number} labellg set the width of label (1-12)
30377  * @cfg {Number} labelmd set the width of label (1-12)
30378  * @cfg {Number} labelsm set the width of label (1-12)
30379  * @cfg {Number} labelxs set the width of label (1-12)
30380
30381  *     
30382  * @constructor
30383  * Create a new DateSplitField
30384  * @param {Object} config The config object
30385  */
30386
30387 Roo.bootstrap.DateSplitField = function(config){
30388     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30389     
30390     this.addEvents({
30391         // raw events
30392          /**
30393          * @event years
30394          * getting the data of years
30395          * @param {Roo.bootstrap.DateSplitField} this
30396          * @param {Object} years
30397          */
30398         "years" : true,
30399         /**
30400          * @event days
30401          * getting the data of days
30402          * @param {Roo.bootstrap.DateSplitField} this
30403          * @param {Object} days
30404          */
30405         "days" : true,
30406         /**
30407          * @event invalid
30408          * Fires after the field has been marked as invalid.
30409          * @param {Roo.form.Field} this
30410          * @param {String} msg The validation message
30411          */
30412         invalid : true,
30413        /**
30414          * @event valid
30415          * Fires after the field has been validated with no errors.
30416          * @param {Roo.form.Field} this
30417          */
30418         valid : true
30419     });
30420 };
30421
30422 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30423     
30424     fieldLabel : '',
30425     labelAlign : 'top',
30426     labelWidth : 3,
30427     dayAllowBlank : false,
30428     monthAllowBlank : false,
30429     yearAllowBlank : false,
30430     dayPlaceholder : '',
30431     monthPlaceholder : '',
30432     yearPlaceholder : '',
30433     dayFormat : 'd',
30434     monthFormat : 'm',
30435     yearFormat : 'Y',
30436     isFormField : true,
30437     labellg : 0,
30438     labelmd : 0,
30439     labelsm : 0,
30440     labelxs : 0,
30441     
30442     getAutoCreate : function()
30443     {
30444         var cfg = {
30445             tag : 'div',
30446             cls : 'row roo-date-split-field-group',
30447             cn : [
30448                 {
30449                     tag : 'input',
30450                     type : 'hidden',
30451                     cls : 'form-hidden-field roo-date-split-field-group-value',
30452                     name : this.name
30453                 }
30454             ]
30455         };
30456         
30457         var labelCls = 'col-md-12';
30458         var contentCls = 'col-md-4';
30459         
30460         if(this.fieldLabel){
30461             
30462             var label = {
30463                 tag : 'div',
30464                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30465                 cn : [
30466                     {
30467                         tag : 'label',
30468                         html : this.fieldLabel
30469                     }
30470                 ]
30471             };
30472             
30473             if(this.labelAlign == 'left'){
30474             
30475                 if(this.labelWidth > 12){
30476                     label.style = "width: " + this.labelWidth + 'px';
30477                 }
30478
30479                 if(this.labelWidth < 13 && this.labelmd == 0){
30480                     this.labelmd = this.labelWidth;
30481                 }
30482
30483                 if(this.labellg > 0){
30484                     labelCls = ' col-lg-' + this.labellg;
30485                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30486                 }
30487
30488                 if(this.labelmd > 0){
30489                     labelCls = ' col-md-' + this.labelmd;
30490                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30491                 }
30492
30493                 if(this.labelsm > 0){
30494                     labelCls = ' col-sm-' + this.labelsm;
30495                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30496                 }
30497
30498                 if(this.labelxs > 0){
30499                     labelCls = ' col-xs-' + this.labelxs;
30500                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30501                 }
30502             }
30503             
30504             label.cls += ' ' + labelCls;
30505             
30506             cfg.cn.push(label);
30507         }
30508         
30509         Roo.each(['day', 'month', 'year'], function(t){
30510             cfg.cn.push({
30511                 tag : 'div',
30512                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30513             });
30514         }, this);
30515         
30516         return cfg;
30517     },
30518     
30519     inputEl: function ()
30520     {
30521         return this.el.select('.roo-date-split-field-group-value', true).first();
30522     },
30523     
30524     onRender : function(ct, position) 
30525     {
30526         var _this = this;
30527         
30528         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30529         
30530         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30531         
30532         this.dayField = new Roo.bootstrap.ComboBox({
30533             allowBlank : this.dayAllowBlank,
30534             alwaysQuery : true,
30535             displayField : 'value',
30536             editable : false,
30537             fieldLabel : '',
30538             forceSelection : true,
30539             mode : 'local',
30540             placeholder : this.dayPlaceholder,
30541             selectOnFocus : true,
30542             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30543             triggerAction : 'all',
30544             typeAhead : true,
30545             valueField : 'value',
30546             store : new Roo.data.SimpleStore({
30547                 data : (function() {    
30548                     var days = [];
30549                     _this.fireEvent('days', _this, days);
30550                     return days;
30551                 })(),
30552                 fields : [ 'value' ]
30553             }),
30554             listeners : {
30555                 select : function (_self, record, index)
30556                 {
30557                     _this.setValue(_this.getValue());
30558                 }
30559             }
30560         });
30561
30562         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30563         
30564         this.monthField = new Roo.bootstrap.MonthField({
30565             after : '<i class=\"fa fa-calendar\"></i>',
30566             allowBlank : this.monthAllowBlank,
30567             placeholder : this.monthPlaceholder,
30568             readOnly : true,
30569             listeners : {
30570                 render : function (_self)
30571                 {
30572                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30573                         e.preventDefault();
30574                         _self.focus();
30575                     });
30576                 },
30577                 select : function (_self, oldvalue, newvalue)
30578                 {
30579                     _this.setValue(_this.getValue());
30580                 }
30581             }
30582         });
30583         
30584         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30585         
30586         this.yearField = new Roo.bootstrap.ComboBox({
30587             allowBlank : this.yearAllowBlank,
30588             alwaysQuery : true,
30589             displayField : 'value',
30590             editable : false,
30591             fieldLabel : '',
30592             forceSelection : true,
30593             mode : 'local',
30594             placeholder : this.yearPlaceholder,
30595             selectOnFocus : true,
30596             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30597             triggerAction : 'all',
30598             typeAhead : true,
30599             valueField : 'value',
30600             store : new Roo.data.SimpleStore({
30601                 data : (function() {
30602                     var years = [];
30603                     _this.fireEvent('years', _this, years);
30604                     return years;
30605                 })(),
30606                 fields : [ 'value' ]
30607             }),
30608             listeners : {
30609                 select : function (_self, record, index)
30610                 {
30611                     _this.setValue(_this.getValue());
30612                 }
30613             }
30614         });
30615
30616         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30617     },
30618     
30619     setValue : function(v, format)
30620     {
30621         this.inputEl.dom.value = v;
30622         
30623         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30624         
30625         var d = Date.parseDate(v, f);
30626         
30627         if(!d){
30628             this.validate();
30629             return;
30630         }
30631         
30632         this.setDay(d.format(this.dayFormat));
30633         this.setMonth(d.format(this.monthFormat));
30634         this.setYear(d.format(this.yearFormat));
30635         
30636         this.validate();
30637         
30638         return;
30639     },
30640     
30641     setDay : function(v)
30642     {
30643         this.dayField.setValue(v);
30644         this.inputEl.dom.value = this.getValue();
30645         this.validate();
30646         return;
30647     },
30648     
30649     setMonth : function(v)
30650     {
30651         this.monthField.setValue(v, true);
30652         this.inputEl.dom.value = this.getValue();
30653         this.validate();
30654         return;
30655     },
30656     
30657     setYear : function(v)
30658     {
30659         this.yearField.setValue(v);
30660         this.inputEl.dom.value = this.getValue();
30661         this.validate();
30662         return;
30663     },
30664     
30665     getDay : function()
30666     {
30667         return this.dayField.getValue();
30668     },
30669     
30670     getMonth : function()
30671     {
30672         return this.monthField.getValue();
30673     },
30674     
30675     getYear : function()
30676     {
30677         return this.yearField.getValue();
30678     },
30679     
30680     getValue : function()
30681     {
30682         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30683         
30684         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30685         
30686         return date;
30687     },
30688     
30689     reset : function()
30690     {
30691         this.setDay('');
30692         this.setMonth('');
30693         this.setYear('');
30694         this.inputEl.dom.value = '';
30695         this.validate();
30696         return;
30697     },
30698     
30699     validate : function()
30700     {
30701         var d = this.dayField.validate();
30702         var m = this.monthField.validate();
30703         var y = this.yearField.validate();
30704         
30705         var valid = true;
30706         
30707         if(
30708                 (!this.dayAllowBlank && !d) ||
30709                 (!this.monthAllowBlank && !m) ||
30710                 (!this.yearAllowBlank && !y)
30711         ){
30712             valid = false;
30713         }
30714         
30715         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30716             return valid;
30717         }
30718         
30719         if(valid){
30720             this.markValid();
30721             return valid;
30722         }
30723         
30724         this.markInvalid();
30725         
30726         return valid;
30727     },
30728     
30729     markValid : function()
30730     {
30731         
30732         var label = this.el.select('label', true).first();
30733         var icon = this.el.select('i.fa-star', true).first();
30734
30735         if(label && icon){
30736             icon.remove();
30737         }
30738         
30739         this.fireEvent('valid', this);
30740     },
30741     
30742      /**
30743      * Mark this field as invalid
30744      * @param {String} msg The validation message
30745      */
30746     markInvalid : function(msg)
30747     {
30748         
30749         var label = this.el.select('label', true).first();
30750         var icon = this.el.select('i.fa-star', true).first();
30751
30752         if(label && !icon){
30753             this.el.select('.roo-date-split-field-label', true).createChild({
30754                 tag : 'i',
30755                 cls : 'text-danger fa fa-lg fa-star',
30756                 tooltip : 'This field is required',
30757                 style : 'margin-right:5px;'
30758             }, label, true);
30759         }
30760         
30761         this.fireEvent('invalid', this, msg);
30762     },
30763     
30764     clearInvalid : function()
30765     {
30766         var label = this.el.select('label', true).first();
30767         var icon = this.el.select('i.fa-star', true).first();
30768
30769         if(label && icon){
30770             icon.remove();
30771         }
30772         
30773         this.fireEvent('valid', this);
30774     },
30775     
30776     getName: function()
30777     {
30778         return this.name;
30779     }
30780     
30781 });
30782
30783  /**
30784  *
30785  * This is based on 
30786  * http://masonry.desandro.com
30787  *
30788  * The idea is to render all the bricks based on vertical width...
30789  *
30790  * The original code extends 'outlayer' - we might need to use that....
30791  * 
30792  */
30793
30794
30795 /**
30796  * @class Roo.bootstrap.LayoutMasonry
30797  * @extends Roo.bootstrap.Component
30798  * Bootstrap Layout Masonry class
30799  * 
30800  * @constructor
30801  * Create a new Element
30802  * @param {Object} config The config object
30803  */
30804
30805 Roo.bootstrap.LayoutMasonry = function(config){
30806     
30807     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30808     
30809     this.bricks = [];
30810     
30811     Roo.bootstrap.LayoutMasonry.register(this);
30812     
30813     this.addEvents({
30814         // raw events
30815         /**
30816          * @event layout
30817          * Fire after layout the items
30818          * @param {Roo.bootstrap.LayoutMasonry} this
30819          * @param {Roo.EventObject} e
30820          */
30821         "layout" : true
30822     });
30823     
30824 };
30825
30826 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30827     
30828     /**
30829      * @cfg {Boolean} isLayoutInstant = no animation?
30830      */   
30831     isLayoutInstant : false, // needed?
30832    
30833     /**
30834      * @cfg {Number} boxWidth  width of the columns
30835      */   
30836     boxWidth : 450,
30837     
30838       /**
30839      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30840      */   
30841     boxHeight : 0,
30842     
30843     /**
30844      * @cfg {Number} padWidth padding below box..
30845      */   
30846     padWidth : 10, 
30847     
30848     /**
30849      * @cfg {Number} gutter gutter width..
30850      */   
30851     gutter : 10,
30852     
30853      /**
30854      * @cfg {Number} maxCols maximum number of columns
30855      */   
30856     
30857     maxCols: 0,
30858     
30859     /**
30860      * @cfg {Boolean} isAutoInitial defalut true
30861      */   
30862     isAutoInitial : true, 
30863     
30864     containerWidth: 0,
30865     
30866     /**
30867      * @cfg {Boolean} isHorizontal defalut false
30868      */   
30869     isHorizontal : false, 
30870
30871     currentSize : null,
30872     
30873     tag: 'div',
30874     
30875     cls: '',
30876     
30877     bricks: null, //CompositeElement
30878     
30879     cols : 1,
30880     
30881     _isLayoutInited : false,
30882     
30883 //    isAlternative : false, // only use for vertical layout...
30884     
30885     /**
30886      * @cfg {Number} alternativePadWidth padding below box..
30887      */   
30888     alternativePadWidth : 50,
30889     
30890     selectedBrick : [],
30891     
30892     getAutoCreate : function(){
30893         
30894         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30895         
30896         var cfg = {
30897             tag: this.tag,
30898             cls: 'blog-masonary-wrapper ' + this.cls,
30899             cn : {
30900                 cls : 'mas-boxes masonary'
30901             }
30902         };
30903         
30904         return cfg;
30905     },
30906     
30907     getChildContainer: function( )
30908     {
30909         if (this.boxesEl) {
30910             return this.boxesEl;
30911         }
30912         
30913         this.boxesEl = this.el.select('.mas-boxes').first();
30914         
30915         return this.boxesEl;
30916     },
30917     
30918     
30919     initEvents : function()
30920     {
30921         var _this = this;
30922         
30923         if(this.isAutoInitial){
30924             Roo.log('hook children rendered');
30925             this.on('childrenrendered', function() {
30926                 Roo.log('children rendered');
30927                 _this.initial();
30928             } ,this);
30929         }
30930     },
30931     
30932     initial : function()
30933     {
30934         this.selectedBrick = [];
30935         
30936         this.currentSize = this.el.getBox(true);
30937         
30938         Roo.EventManager.onWindowResize(this.resize, this); 
30939
30940         if(!this.isAutoInitial){
30941             this.layout();
30942             return;
30943         }
30944         
30945         this.layout();
30946         
30947         return;
30948         //this.layout.defer(500,this);
30949         
30950     },
30951     
30952     resize : function()
30953     {
30954         var cs = this.el.getBox(true);
30955         
30956         if (
30957                 this.currentSize.width == cs.width && 
30958                 this.currentSize.x == cs.x && 
30959                 this.currentSize.height == cs.height && 
30960                 this.currentSize.y == cs.y 
30961         ) {
30962             Roo.log("no change in with or X or Y");
30963             return;
30964         }
30965         
30966         this.currentSize = cs;
30967         
30968         this.layout();
30969         
30970     },
30971     
30972     layout : function()
30973     {   
30974         this._resetLayout();
30975         
30976         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30977         
30978         this.layoutItems( isInstant );
30979       
30980         this._isLayoutInited = true;
30981         
30982         this.fireEvent('layout', this);
30983         
30984     },
30985     
30986     _resetLayout : function()
30987     {
30988         if(this.isHorizontal){
30989             this.horizontalMeasureColumns();
30990             return;
30991         }
30992         
30993         this.verticalMeasureColumns();
30994         
30995     },
30996     
30997     verticalMeasureColumns : function()
30998     {
30999         this.getContainerWidth();
31000         
31001 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31002 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31003 //            return;
31004 //        }
31005         
31006         var boxWidth = this.boxWidth + this.padWidth;
31007         
31008         if(this.containerWidth < this.boxWidth){
31009             boxWidth = this.containerWidth
31010         }
31011         
31012         var containerWidth = this.containerWidth;
31013         
31014         var cols = Math.floor(containerWidth / boxWidth);
31015         
31016         this.cols = Math.max( cols, 1 );
31017         
31018         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31019         
31020         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31021         
31022         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31023         
31024         this.colWidth = boxWidth + avail - this.padWidth;
31025         
31026         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31027         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31028     },
31029     
31030     horizontalMeasureColumns : function()
31031     {
31032         this.getContainerWidth();
31033         
31034         var boxWidth = this.boxWidth;
31035         
31036         if(this.containerWidth < boxWidth){
31037             boxWidth = this.containerWidth;
31038         }
31039         
31040         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31041         
31042         this.el.setHeight(boxWidth);
31043         
31044     },
31045     
31046     getContainerWidth : function()
31047     {
31048         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31049     },
31050     
31051     layoutItems : function( isInstant )
31052     {
31053         Roo.log(this.bricks);
31054         
31055         var items = Roo.apply([], this.bricks);
31056         
31057         if(this.isHorizontal){
31058             this._horizontalLayoutItems( items , isInstant );
31059             return;
31060         }
31061         
31062 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31063 //            this._verticalAlternativeLayoutItems( items , isInstant );
31064 //            return;
31065 //        }
31066         
31067         this._verticalLayoutItems( items , isInstant );
31068         
31069     },
31070     
31071     _verticalLayoutItems : function ( items , isInstant)
31072     {
31073         if ( !items || !items.length ) {
31074             return;
31075         }
31076         
31077         var standard = [
31078             ['xs', 'xs', 'xs', 'tall'],
31079             ['xs', 'xs', 'tall'],
31080             ['xs', 'xs', 'sm'],
31081             ['xs', 'xs', 'xs'],
31082             ['xs', 'tall'],
31083             ['xs', 'sm'],
31084             ['xs', 'xs'],
31085             ['xs'],
31086             
31087             ['sm', 'xs', 'xs'],
31088             ['sm', 'xs'],
31089             ['sm'],
31090             
31091             ['tall', 'xs', 'xs', 'xs'],
31092             ['tall', 'xs', 'xs'],
31093             ['tall', 'xs'],
31094             ['tall']
31095             
31096         ];
31097         
31098         var queue = [];
31099         
31100         var boxes = [];
31101         
31102         var box = [];
31103         
31104         Roo.each(items, function(item, k){
31105             
31106             switch (item.size) {
31107                 // these layouts take up a full box,
31108                 case 'md' :
31109                 case 'md-left' :
31110                 case 'md-right' :
31111                 case 'wide' :
31112                     
31113                     if(box.length){
31114                         boxes.push(box);
31115                         box = [];
31116                     }
31117                     
31118                     boxes.push([item]);
31119                     
31120                     break;
31121                     
31122                 case 'xs' :
31123                 case 'sm' :
31124                 case 'tall' :
31125                     
31126                     box.push(item);
31127                     
31128                     break;
31129                 default :
31130                     break;
31131                     
31132             }
31133             
31134         }, this);
31135         
31136         if(box.length){
31137             boxes.push(box);
31138             box = [];
31139         }
31140         
31141         var filterPattern = function(box, length)
31142         {
31143             if(!box.length){
31144                 return;
31145             }
31146             
31147             var match = false;
31148             
31149             var pattern = box.slice(0, length);
31150             
31151             var format = [];
31152             
31153             Roo.each(pattern, function(i){
31154                 format.push(i.size);
31155             }, this);
31156             
31157             Roo.each(standard, function(s){
31158                 
31159                 if(String(s) != String(format)){
31160                     return;
31161                 }
31162                 
31163                 match = true;
31164                 return false;
31165                 
31166             }, this);
31167             
31168             if(!match && length == 1){
31169                 return;
31170             }
31171             
31172             if(!match){
31173                 filterPattern(box, length - 1);
31174                 return;
31175             }
31176                 
31177             queue.push(pattern);
31178
31179             box = box.slice(length, box.length);
31180
31181             filterPattern(box, 4);
31182
31183             return;
31184             
31185         }
31186         
31187         Roo.each(boxes, function(box, k){
31188             
31189             if(!box.length){
31190                 return;
31191             }
31192             
31193             if(box.length == 1){
31194                 queue.push(box);
31195                 return;
31196             }
31197             
31198             filterPattern(box, 4);
31199             
31200         }, this);
31201         
31202         this._processVerticalLayoutQueue( queue, isInstant );
31203         
31204     },
31205     
31206 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31207 //    {
31208 //        if ( !items || !items.length ) {
31209 //            return;
31210 //        }
31211 //
31212 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31213 //        
31214 //    },
31215     
31216     _horizontalLayoutItems : function ( items , isInstant)
31217     {
31218         if ( !items || !items.length || items.length < 3) {
31219             return;
31220         }
31221         
31222         items.reverse();
31223         
31224         var eItems = items.slice(0, 3);
31225         
31226         items = items.slice(3, items.length);
31227         
31228         var standard = [
31229             ['xs', 'xs', 'xs', 'wide'],
31230             ['xs', 'xs', 'wide'],
31231             ['xs', 'xs', 'sm'],
31232             ['xs', 'xs', 'xs'],
31233             ['xs', 'wide'],
31234             ['xs', 'sm'],
31235             ['xs', 'xs'],
31236             ['xs'],
31237             
31238             ['sm', 'xs', 'xs'],
31239             ['sm', 'xs'],
31240             ['sm'],
31241             
31242             ['wide', 'xs', 'xs', 'xs'],
31243             ['wide', 'xs', 'xs'],
31244             ['wide', 'xs'],
31245             ['wide'],
31246             
31247             ['wide-thin']
31248         ];
31249         
31250         var queue = [];
31251         
31252         var boxes = [];
31253         
31254         var box = [];
31255         
31256         Roo.each(items, function(item, k){
31257             
31258             switch (item.size) {
31259                 case 'md' :
31260                 case 'md-left' :
31261                 case 'md-right' :
31262                 case 'tall' :
31263                     
31264                     if(box.length){
31265                         boxes.push(box);
31266                         box = [];
31267                     }
31268                     
31269                     boxes.push([item]);
31270                     
31271                     break;
31272                     
31273                 case 'xs' :
31274                 case 'sm' :
31275                 case 'wide' :
31276                 case 'wide-thin' :
31277                     
31278                     box.push(item);
31279                     
31280                     break;
31281                 default :
31282                     break;
31283                     
31284             }
31285             
31286         }, this);
31287         
31288         if(box.length){
31289             boxes.push(box);
31290             box = [];
31291         }
31292         
31293         var filterPattern = function(box, length)
31294         {
31295             if(!box.length){
31296                 return;
31297             }
31298             
31299             var match = false;
31300             
31301             var pattern = box.slice(0, length);
31302             
31303             var format = [];
31304             
31305             Roo.each(pattern, function(i){
31306                 format.push(i.size);
31307             }, this);
31308             
31309             Roo.each(standard, function(s){
31310                 
31311                 if(String(s) != String(format)){
31312                     return;
31313                 }
31314                 
31315                 match = true;
31316                 return false;
31317                 
31318             }, this);
31319             
31320             if(!match && length == 1){
31321                 return;
31322             }
31323             
31324             if(!match){
31325                 filterPattern(box, length - 1);
31326                 return;
31327             }
31328                 
31329             queue.push(pattern);
31330
31331             box = box.slice(length, box.length);
31332
31333             filterPattern(box, 4);
31334
31335             return;
31336             
31337         }
31338         
31339         Roo.each(boxes, function(box, k){
31340             
31341             if(!box.length){
31342                 return;
31343             }
31344             
31345             if(box.length == 1){
31346                 queue.push(box);
31347                 return;
31348             }
31349             
31350             filterPattern(box, 4);
31351             
31352         }, this);
31353         
31354         
31355         var prune = [];
31356         
31357         var pos = this.el.getBox(true);
31358         
31359         var minX = pos.x;
31360         
31361         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31362         
31363         var hit_end = false;
31364         
31365         Roo.each(queue, function(box){
31366             
31367             if(hit_end){
31368                 
31369                 Roo.each(box, function(b){
31370                 
31371                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31372                     b.el.hide();
31373
31374                 }, this);
31375
31376                 return;
31377             }
31378             
31379             var mx = 0;
31380             
31381             Roo.each(box, function(b){
31382                 
31383                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31384                 b.el.show();
31385
31386                 mx = Math.max(mx, b.x);
31387                 
31388             }, this);
31389             
31390             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31391             
31392             if(maxX < minX){
31393                 
31394                 Roo.each(box, function(b){
31395                 
31396                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31397                     b.el.hide();
31398                     
31399                 }, this);
31400                 
31401                 hit_end = true;
31402                 
31403                 return;
31404             }
31405             
31406             prune.push(box);
31407             
31408         }, this);
31409         
31410         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31411     },
31412     
31413     /** Sets position of item in DOM
31414     * @param {Element} item
31415     * @param {Number} x - horizontal position
31416     * @param {Number} y - vertical position
31417     * @param {Boolean} isInstant - disables transitions
31418     */
31419     _processVerticalLayoutQueue : function( queue, isInstant )
31420     {
31421         var pos = this.el.getBox(true);
31422         var x = pos.x;
31423         var y = pos.y;
31424         var maxY = [];
31425         
31426         for (var i = 0; i < this.cols; i++){
31427             maxY[i] = pos.y;
31428         }
31429         
31430         Roo.each(queue, function(box, k){
31431             
31432             var col = k % this.cols;
31433             
31434             Roo.each(box, function(b,kk){
31435                 
31436                 b.el.position('absolute');
31437                 
31438                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31439                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31440                 
31441                 if(b.size == 'md-left' || b.size == 'md-right'){
31442                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31443                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31444                 }
31445                 
31446                 b.el.setWidth(width);
31447                 b.el.setHeight(height);
31448                 // iframe?
31449                 b.el.select('iframe',true).setSize(width,height);
31450                 
31451             }, this);
31452             
31453             for (var i = 0; i < this.cols; i++){
31454                 
31455                 if(maxY[i] < maxY[col]){
31456                     col = i;
31457                     continue;
31458                 }
31459                 
31460                 col = Math.min(col, i);
31461                 
31462             }
31463             
31464             x = pos.x + col * (this.colWidth + this.padWidth);
31465             
31466             y = maxY[col];
31467             
31468             var positions = [];
31469             
31470             switch (box.length){
31471                 case 1 :
31472                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31473                     break;
31474                 case 2 :
31475                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31476                     break;
31477                 case 3 :
31478                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31479                     break;
31480                 case 4 :
31481                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31482                     break;
31483                 default :
31484                     break;
31485             }
31486             
31487             Roo.each(box, function(b,kk){
31488                 
31489                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31490                 
31491                 var sz = b.el.getSize();
31492                 
31493                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31494                 
31495             }, this);
31496             
31497         }, this);
31498         
31499         var mY = 0;
31500         
31501         for (var i = 0; i < this.cols; i++){
31502             mY = Math.max(mY, maxY[i]);
31503         }
31504         
31505         this.el.setHeight(mY - pos.y);
31506         
31507     },
31508     
31509 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31510 //    {
31511 //        var pos = this.el.getBox(true);
31512 //        var x = pos.x;
31513 //        var y = pos.y;
31514 //        var maxX = pos.right;
31515 //        
31516 //        var maxHeight = 0;
31517 //        
31518 //        Roo.each(items, function(item, k){
31519 //            
31520 //            var c = k % 2;
31521 //            
31522 //            item.el.position('absolute');
31523 //                
31524 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31525 //
31526 //            item.el.setWidth(width);
31527 //
31528 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31529 //
31530 //            item.el.setHeight(height);
31531 //            
31532 //            if(c == 0){
31533 //                item.el.setXY([x, y], isInstant ? false : true);
31534 //            } else {
31535 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31536 //            }
31537 //            
31538 //            y = y + height + this.alternativePadWidth;
31539 //            
31540 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31541 //            
31542 //        }, this);
31543 //        
31544 //        this.el.setHeight(maxHeight);
31545 //        
31546 //    },
31547     
31548     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31549     {
31550         var pos = this.el.getBox(true);
31551         
31552         var minX = pos.x;
31553         var minY = pos.y;
31554         
31555         var maxX = pos.right;
31556         
31557         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31558         
31559         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31560         
31561         Roo.each(queue, function(box, k){
31562             
31563             Roo.each(box, function(b, kk){
31564                 
31565                 b.el.position('absolute');
31566                 
31567                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31568                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31569                 
31570                 if(b.size == 'md-left' || b.size == 'md-right'){
31571                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31572                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31573                 }
31574                 
31575                 b.el.setWidth(width);
31576                 b.el.setHeight(height);
31577                 
31578             }, this);
31579             
31580             if(!box.length){
31581                 return;
31582             }
31583             
31584             var positions = [];
31585             
31586             switch (box.length){
31587                 case 1 :
31588                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31589                     break;
31590                 case 2 :
31591                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31592                     break;
31593                 case 3 :
31594                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31595                     break;
31596                 case 4 :
31597                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31598                     break;
31599                 default :
31600                     break;
31601             }
31602             
31603             Roo.each(box, function(b,kk){
31604                 
31605                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31606                 
31607                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31608                 
31609             }, this);
31610             
31611         }, this);
31612         
31613     },
31614     
31615     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31616     {
31617         Roo.each(eItems, function(b,k){
31618             
31619             b.size = (k == 0) ? 'sm' : 'xs';
31620             b.x = (k == 0) ? 2 : 1;
31621             b.y = (k == 0) ? 2 : 1;
31622             
31623             b.el.position('absolute');
31624             
31625             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31626                 
31627             b.el.setWidth(width);
31628             
31629             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31630             
31631             b.el.setHeight(height);
31632             
31633         }, this);
31634
31635         var positions = [];
31636         
31637         positions.push({
31638             x : maxX - this.unitWidth * 2 - this.gutter,
31639             y : minY
31640         });
31641         
31642         positions.push({
31643             x : maxX - this.unitWidth,
31644             y : minY + (this.unitWidth + this.gutter) * 2
31645         });
31646         
31647         positions.push({
31648             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31649             y : minY
31650         });
31651         
31652         Roo.each(eItems, function(b,k){
31653             
31654             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31655
31656         }, this);
31657         
31658     },
31659     
31660     getVerticalOneBoxColPositions : function(x, y, box)
31661     {
31662         var pos = [];
31663         
31664         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31665         
31666         if(box[0].size == 'md-left'){
31667             rand = 0;
31668         }
31669         
31670         if(box[0].size == 'md-right'){
31671             rand = 1;
31672         }
31673         
31674         pos.push({
31675             x : x + (this.unitWidth + this.gutter) * rand,
31676             y : y
31677         });
31678         
31679         return pos;
31680     },
31681     
31682     getVerticalTwoBoxColPositions : function(x, y, box)
31683     {
31684         var pos = [];
31685         
31686         if(box[0].size == 'xs'){
31687             
31688             pos.push({
31689                 x : x,
31690                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31691             });
31692
31693             pos.push({
31694                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31695                 y : y
31696             });
31697             
31698             return pos;
31699             
31700         }
31701         
31702         pos.push({
31703             x : x,
31704             y : y
31705         });
31706
31707         pos.push({
31708             x : x + (this.unitWidth + this.gutter) * 2,
31709             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31710         });
31711         
31712         return pos;
31713         
31714     },
31715     
31716     getVerticalThreeBoxColPositions : function(x, y, box)
31717     {
31718         var pos = [];
31719         
31720         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31721             
31722             pos.push({
31723                 x : x,
31724                 y : y
31725             });
31726
31727             pos.push({
31728                 x : x + (this.unitWidth + this.gutter) * 1,
31729                 y : y
31730             });
31731             
31732             pos.push({
31733                 x : x + (this.unitWidth + this.gutter) * 2,
31734                 y : y
31735             });
31736             
31737             return pos;
31738             
31739         }
31740         
31741         if(box[0].size == 'xs' && box[1].size == 'xs'){
31742             
31743             pos.push({
31744                 x : x,
31745                 y : y
31746             });
31747
31748             pos.push({
31749                 x : x,
31750                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31751             });
31752             
31753             pos.push({
31754                 x : x + (this.unitWidth + this.gutter) * 1,
31755                 y : y
31756             });
31757             
31758             return pos;
31759             
31760         }
31761         
31762         pos.push({
31763             x : x,
31764             y : y
31765         });
31766
31767         pos.push({
31768             x : x + (this.unitWidth + this.gutter) * 2,
31769             y : y
31770         });
31771
31772         pos.push({
31773             x : x + (this.unitWidth + this.gutter) * 2,
31774             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31775         });
31776             
31777         return pos;
31778         
31779     },
31780     
31781     getVerticalFourBoxColPositions : function(x, y, box)
31782     {
31783         var pos = [];
31784         
31785         if(box[0].size == 'xs'){
31786             
31787             pos.push({
31788                 x : x,
31789                 y : y
31790             });
31791
31792             pos.push({
31793                 x : x,
31794                 y : y + (this.unitHeight + this.gutter) * 1
31795             });
31796             
31797             pos.push({
31798                 x : x,
31799                 y : y + (this.unitHeight + this.gutter) * 2
31800             });
31801             
31802             pos.push({
31803                 x : x + (this.unitWidth + this.gutter) * 1,
31804                 y : y
31805             });
31806             
31807             return pos;
31808             
31809         }
31810         
31811         pos.push({
31812             x : x,
31813             y : y
31814         });
31815
31816         pos.push({
31817             x : x + (this.unitWidth + this.gutter) * 2,
31818             y : y
31819         });
31820
31821         pos.push({
31822             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31823             y : y + (this.unitHeight + this.gutter) * 1
31824         });
31825
31826         pos.push({
31827             x : x + (this.unitWidth + this.gutter) * 2,
31828             y : y + (this.unitWidth + this.gutter) * 2
31829         });
31830
31831         return pos;
31832         
31833     },
31834     
31835     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31836     {
31837         var pos = [];
31838         
31839         if(box[0].size == 'md-left'){
31840             pos.push({
31841                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31842                 y : minY
31843             });
31844             
31845             return pos;
31846         }
31847         
31848         if(box[0].size == 'md-right'){
31849             pos.push({
31850                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31851                 y : minY + (this.unitWidth + this.gutter) * 1
31852             });
31853             
31854             return pos;
31855         }
31856         
31857         var rand = Math.floor(Math.random() * (4 - box[0].y));
31858         
31859         pos.push({
31860             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31861             y : minY + (this.unitWidth + this.gutter) * rand
31862         });
31863         
31864         return pos;
31865         
31866     },
31867     
31868     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31869     {
31870         var pos = [];
31871         
31872         if(box[0].size == 'xs'){
31873             
31874             pos.push({
31875                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31876                 y : minY
31877             });
31878
31879             pos.push({
31880                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31881                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31882             });
31883             
31884             return pos;
31885             
31886         }
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) * 2
31896         });
31897         
31898         return pos;
31899         
31900     },
31901     
31902     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31903     {
31904         var pos = [];
31905         
31906         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31907             
31908             pos.push({
31909                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31910                 y : minY
31911             });
31912
31913             pos.push({
31914                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31915                 y : minY + (this.unitWidth + this.gutter) * 1
31916             });
31917             
31918             pos.push({
31919                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31920                 y : minY + (this.unitWidth + this.gutter) * 2
31921             });
31922             
31923             return pos;
31924             
31925         }
31926         
31927         if(box[0].size == 'xs' && box[1].size == 'xs'){
31928             
31929             pos.push({
31930                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31931                 y : minY
31932             });
31933
31934             pos.push({
31935                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31936                 y : minY
31937             });
31938             
31939             pos.push({
31940                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31941                 y : minY + (this.unitWidth + this.gutter) * 1
31942             });
31943             
31944             return pos;
31945             
31946         }
31947         
31948         pos.push({
31949             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31950             y : minY
31951         });
31952
31953         pos.push({
31954             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31955             y : minY + (this.unitWidth + this.gutter) * 2
31956         });
31957
31958         pos.push({
31959             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31960             y : minY + (this.unitWidth + this.gutter) * 2
31961         });
31962             
31963         return pos;
31964         
31965     },
31966     
31967     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31968     {
31969         var pos = [];
31970         
31971         if(box[0].size == 'xs'){
31972             
31973             pos.push({
31974                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31975                 y : minY
31976             });
31977
31978             pos.push({
31979                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31980                 y : minY
31981             });
31982             
31983             pos.push({
31984                 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),
31985                 y : minY
31986             });
31987             
31988             pos.push({
31989                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31990                 y : minY + (this.unitWidth + this.gutter) * 1
31991             });
31992             
31993             return pos;
31994             
31995         }
31996         
31997         pos.push({
31998             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31999             y : minY
32000         });
32001         
32002         pos.push({
32003             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32004             y : minY + (this.unitWidth + this.gutter) * 2
32005         });
32006         
32007         pos.push({
32008             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32009             y : minY + (this.unitWidth + this.gutter) * 2
32010         });
32011         
32012         pos.push({
32013             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32014             y : minY + (this.unitWidth + this.gutter) * 2
32015         });
32016
32017         return pos;
32018         
32019     },
32020     
32021     /**
32022     * remove a Masonry Brick
32023     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32024     */
32025     removeBrick : function(brick_id)
32026     {
32027         if (!brick_id) {
32028             return;
32029         }
32030         
32031         for (var i = 0; i<this.bricks.length; i++) {
32032             if (this.bricks[i].id == brick_id) {
32033                 this.bricks.splice(i,1);
32034                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32035                 this.initial();
32036             }
32037         }
32038     },
32039     
32040     /**
32041     * adds a Masonry Brick
32042     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32043     */
32044     addBrick : function(cfg)
32045     {
32046         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32047         //this.register(cn);
32048         cn.parentId = this.id;
32049         cn.render(this.el);
32050         return cn;
32051     },
32052     
32053     /**
32054     * register a Masonry Brick
32055     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32056     */
32057     
32058     register : function(brick)
32059     {
32060         this.bricks.push(brick);
32061         brick.masonryId = this.id;
32062     },
32063     
32064     /**
32065     * clear all the Masonry Brick
32066     */
32067     clearAll : function()
32068     {
32069         this.bricks = [];
32070         //this.getChildContainer().dom.innerHTML = "";
32071         this.el.dom.innerHTML = '';
32072     },
32073     
32074     getSelected : function()
32075     {
32076         if (!this.selectedBrick) {
32077             return false;
32078         }
32079         
32080         return this.selectedBrick;
32081     }
32082 });
32083
32084 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32085     
32086     groups: {},
32087      /**
32088     * register a Masonry Layout
32089     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32090     */
32091     
32092     register : function(layout)
32093     {
32094         this.groups[layout.id] = layout;
32095     },
32096     /**
32097     * fetch a  Masonry Layout based on the masonry layout ID
32098     * @param {string} the masonry layout to add
32099     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32100     */
32101     
32102     get: function(layout_id) {
32103         if (typeof(this.groups[layout_id]) == 'undefined') {
32104             return false;
32105         }
32106         return this.groups[layout_id] ;
32107     }
32108     
32109     
32110     
32111 });
32112
32113  
32114
32115  /**
32116  *
32117  * This is based on 
32118  * http://masonry.desandro.com
32119  *
32120  * The idea is to render all the bricks based on vertical width...
32121  *
32122  * The original code extends 'outlayer' - we might need to use that....
32123  * 
32124  */
32125
32126
32127 /**
32128  * @class Roo.bootstrap.LayoutMasonryAuto
32129  * @extends Roo.bootstrap.Component
32130  * Bootstrap Layout Masonry class
32131  * 
32132  * @constructor
32133  * Create a new Element
32134  * @param {Object} config The config object
32135  */
32136
32137 Roo.bootstrap.LayoutMasonryAuto = function(config){
32138     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32139 };
32140
32141 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32142     
32143       /**
32144      * @cfg {Boolean} isFitWidth  - resize the width..
32145      */   
32146     isFitWidth : false,  // options..
32147     /**
32148      * @cfg {Boolean} isOriginLeft = left align?
32149      */   
32150     isOriginLeft : true,
32151     /**
32152      * @cfg {Boolean} isOriginTop = top align?
32153      */   
32154     isOriginTop : false,
32155     /**
32156      * @cfg {Boolean} isLayoutInstant = no animation?
32157      */   
32158     isLayoutInstant : false, // needed?
32159     /**
32160      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32161      */   
32162     isResizingContainer : true,
32163     /**
32164      * @cfg {Number} columnWidth  width of the columns 
32165      */   
32166     
32167     columnWidth : 0,
32168     
32169     /**
32170      * @cfg {Number} maxCols maximum number of columns
32171      */   
32172     
32173     maxCols: 0,
32174     /**
32175      * @cfg {Number} padHeight padding below box..
32176      */   
32177     
32178     padHeight : 10, 
32179     
32180     /**
32181      * @cfg {Boolean} isAutoInitial defalut true
32182      */   
32183     
32184     isAutoInitial : true, 
32185     
32186     // private?
32187     gutter : 0,
32188     
32189     containerWidth: 0,
32190     initialColumnWidth : 0,
32191     currentSize : null,
32192     
32193     colYs : null, // array.
32194     maxY : 0,
32195     padWidth: 10,
32196     
32197     
32198     tag: 'div',
32199     cls: '',
32200     bricks: null, //CompositeElement
32201     cols : 0, // array?
32202     // element : null, // wrapped now this.el
32203     _isLayoutInited : null, 
32204     
32205     
32206     getAutoCreate : function(){
32207         
32208         var cfg = {
32209             tag: this.tag,
32210             cls: 'blog-masonary-wrapper ' + this.cls,
32211             cn : {
32212                 cls : 'mas-boxes masonary'
32213             }
32214         };
32215         
32216         return cfg;
32217     },
32218     
32219     getChildContainer: function( )
32220     {
32221         if (this.boxesEl) {
32222             return this.boxesEl;
32223         }
32224         
32225         this.boxesEl = this.el.select('.mas-boxes').first();
32226         
32227         return this.boxesEl;
32228     },
32229     
32230     
32231     initEvents : function()
32232     {
32233         var _this = this;
32234         
32235         if(this.isAutoInitial){
32236             Roo.log('hook children rendered');
32237             this.on('childrenrendered', function() {
32238                 Roo.log('children rendered');
32239                 _this.initial();
32240             } ,this);
32241         }
32242         
32243     },
32244     
32245     initial : function()
32246     {
32247         this.reloadItems();
32248
32249         this.currentSize = this.el.getBox(true);
32250
32251         /// was window resize... - let's see if this works..
32252         Roo.EventManager.onWindowResize(this.resize, this); 
32253
32254         if(!this.isAutoInitial){
32255             this.layout();
32256             return;
32257         }
32258         
32259         this.layout.defer(500,this);
32260     },
32261     
32262     reloadItems: function()
32263     {
32264         this.bricks = this.el.select('.masonry-brick', true);
32265         
32266         this.bricks.each(function(b) {
32267             //Roo.log(b.getSize());
32268             if (!b.attr('originalwidth')) {
32269                 b.attr('originalwidth',  b.getSize().width);
32270             }
32271             
32272         });
32273         
32274         Roo.log(this.bricks.elements.length);
32275     },
32276     
32277     resize : function()
32278     {
32279         Roo.log('resize');
32280         var cs = this.el.getBox(true);
32281         
32282         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32283             Roo.log("no change in with or X");
32284             return;
32285         }
32286         this.currentSize = cs;
32287         this.layout();
32288     },
32289     
32290     layout : function()
32291     {
32292          Roo.log('layout');
32293         this._resetLayout();
32294         //this._manageStamps();
32295       
32296         // don't animate first layout
32297         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32298         this.layoutItems( isInstant );
32299       
32300         // flag for initalized
32301         this._isLayoutInited = true;
32302     },
32303     
32304     layoutItems : function( isInstant )
32305     {
32306         //var items = this._getItemsForLayout( this.items );
32307         // original code supports filtering layout items.. we just ignore it..
32308         
32309         this._layoutItems( this.bricks , isInstant );
32310       
32311         this._postLayout();
32312     },
32313     _layoutItems : function ( items , isInstant)
32314     {
32315        //this.fireEvent( 'layout', this, items );
32316     
32317
32318         if ( !items || !items.elements.length ) {
32319           // no items, emit event with empty array
32320             return;
32321         }
32322
32323         var queue = [];
32324         items.each(function(item) {
32325             Roo.log("layout item");
32326             Roo.log(item);
32327             // get x/y object from method
32328             var position = this._getItemLayoutPosition( item );
32329             // enqueue
32330             position.item = item;
32331             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32332             queue.push( position );
32333         }, this);
32334       
32335         this._processLayoutQueue( queue );
32336     },
32337     /** Sets position of item in DOM
32338     * @param {Element} item
32339     * @param {Number} x - horizontal position
32340     * @param {Number} y - vertical position
32341     * @param {Boolean} isInstant - disables transitions
32342     */
32343     _processLayoutQueue : function( queue )
32344     {
32345         for ( var i=0, len = queue.length; i < len; i++ ) {
32346             var obj = queue[i];
32347             obj.item.position('absolute');
32348             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32349         }
32350     },
32351       
32352     
32353     /**
32354     * Any logic you want to do after each layout,
32355     * i.e. size the container
32356     */
32357     _postLayout : function()
32358     {
32359         this.resizeContainer();
32360     },
32361     
32362     resizeContainer : function()
32363     {
32364         if ( !this.isResizingContainer ) {
32365             return;
32366         }
32367         var size = this._getContainerSize();
32368         if ( size ) {
32369             this.el.setSize(size.width,size.height);
32370             this.boxesEl.setSize(size.width,size.height);
32371         }
32372     },
32373     
32374     
32375     
32376     _resetLayout : function()
32377     {
32378         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32379         this.colWidth = this.el.getWidth();
32380         //this.gutter = this.el.getWidth(); 
32381         
32382         this.measureColumns();
32383
32384         // reset column Y
32385         var i = this.cols;
32386         this.colYs = [];
32387         while (i--) {
32388             this.colYs.push( 0 );
32389         }
32390     
32391         this.maxY = 0;
32392     },
32393
32394     measureColumns : function()
32395     {
32396         this.getContainerWidth();
32397       // if columnWidth is 0, default to outerWidth of first item
32398         if ( !this.columnWidth ) {
32399             var firstItem = this.bricks.first();
32400             Roo.log(firstItem);
32401             this.columnWidth  = this.containerWidth;
32402             if (firstItem && firstItem.attr('originalwidth') ) {
32403                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32404             }
32405             // columnWidth fall back to item of first element
32406             Roo.log("set column width?");
32407                         this.initialColumnWidth = this.columnWidth  ;
32408
32409             // if first elem has no width, default to size of container
32410             
32411         }
32412         
32413         
32414         if (this.initialColumnWidth) {
32415             this.columnWidth = this.initialColumnWidth;
32416         }
32417         
32418         
32419             
32420         // column width is fixed at the top - however if container width get's smaller we should
32421         // reduce it...
32422         
32423         // this bit calcs how man columns..
32424             
32425         var columnWidth = this.columnWidth += this.gutter;
32426       
32427         // calculate columns
32428         var containerWidth = this.containerWidth + this.gutter;
32429         
32430         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32431         // fix rounding errors, typically with gutters
32432         var excess = columnWidth - containerWidth % columnWidth;
32433         
32434         
32435         // if overshoot is less than a pixel, round up, otherwise floor it
32436         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32437         cols = Math[ mathMethod ]( cols );
32438         this.cols = Math.max( cols, 1 );
32439         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32440         
32441          // padding positioning..
32442         var totalColWidth = this.cols * this.columnWidth;
32443         var padavail = this.containerWidth - totalColWidth;
32444         // so for 2 columns - we need 3 'pads'
32445         
32446         var padNeeded = (1+this.cols) * this.padWidth;
32447         
32448         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32449         
32450         this.columnWidth += padExtra
32451         //this.padWidth = Math.floor(padavail /  ( this.cols));
32452         
32453         // adjust colum width so that padding is fixed??
32454         
32455         // we have 3 columns ... total = width * 3
32456         // we have X left over... that should be used by 
32457         
32458         //if (this.expandC) {
32459             
32460         //}
32461         
32462         
32463         
32464     },
32465     
32466     getContainerWidth : function()
32467     {
32468        /* // container is parent if fit width
32469         var container = this.isFitWidth ? this.element.parentNode : this.element;
32470         // check that this.size and size are there
32471         // IE8 triggers resize on body size change, so they might not be
32472         
32473         var size = getSize( container );  //FIXME
32474         this.containerWidth = size && size.innerWidth; //FIXME
32475         */
32476          
32477         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32478         
32479     },
32480     
32481     _getItemLayoutPosition : function( item )  // what is item?
32482     {
32483         // we resize the item to our columnWidth..
32484       
32485         item.setWidth(this.columnWidth);
32486         item.autoBoxAdjust  = false;
32487         
32488         var sz = item.getSize();
32489  
32490         // how many columns does this brick span
32491         var remainder = this.containerWidth % this.columnWidth;
32492         
32493         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32494         // round if off by 1 pixel, otherwise use ceil
32495         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32496         colSpan = Math.min( colSpan, this.cols );
32497         
32498         // normally this should be '1' as we dont' currently allow multi width columns..
32499         
32500         var colGroup = this._getColGroup( colSpan );
32501         // get the minimum Y value from the columns
32502         var minimumY = Math.min.apply( Math, colGroup );
32503         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32504         
32505         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32506          
32507         // position the brick
32508         var position = {
32509             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32510             y: this.currentSize.y + minimumY + this.padHeight
32511         };
32512         
32513         Roo.log(position);
32514         // apply setHeight to necessary columns
32515         var setHeight = minimumY + sz.height + this.padHeight;
32516         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32517         
32518         var setSpan = this.cols + 1 - colGroup.length;
32519         for ( var i = 0; i < setSpan; i++ ) {
32520           this.colYs[ shortColIndex + i ] = setHeight ;
32521         }
32522       
32523         return position;
32524     },
32525     
32526     /**
32527      * @param {Number} colSpan - number of columns the element spans
32528      * @returns {Array} colGroup
32529      */
32530     _getColGroup : function( colSpan )
32531     {
32532         if ( colSpan < 2 ) {
32533           // if brick spans only one column, use all the column Ys
32534           return this.colYs;
32535         }
32536       
32537         var colGroup = [];
32538         // how many different places could this brick fit horizontally
32539         var groupCount = this.cols + 1 - colSpan;
32540         // for each group potential horizontal position
32541         for ( var i = 0; i < groupCount; i++ ) {
32542           // make an array of colY values for that one group
32543           var groupColYs = this.colYs.slice( i, i + colSpan );
32544           // and get the max value of the array
32545           colGroup[i] = Math.max.apply( Math, groupColYs );
32546         }
32547         return colGroup;
32548     },
32549     /*
32550     _manageStamp : function( stamp )
32551     {
32552         var stampSize =  stamp.getSize();
32553         var offset = stamp.getBox();
32554         // get the columns that this stamp affects
32555         var firstX = this.isOriginLeft ? offset.x : offset.right;
32556         var lastX = firstX + stampSize.width;
32557         var firstCol = Math.floor( firstX / this.columnWidth );
32558         firstCol = Math.max( 0, firstCol );
32559         
32560         var lastCol = Math.floor( lastX / this.columnWidth );
32561         // lastCol should not go over if multiple of columnWidth #425
32562         lastCol -= lastX % this.columnWidth ? 0 : 1;
32563         lastCol = Math.min( this.cols - 1, lastCol );
32564         
32565         // set colYs to bottom of the stamp
32566         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32567             stampSize.height;
32568             
32569         for ( var i = firstCol; i <= lastCol; i++ ) {
32570           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32571         }
32572     },
32573     */
32574     
32575     _getContainerSize : function()
32576     {
32577         this.maxY = Math.max.apply( Math, this.colYs );
32578         var size = {
32579             height: this.maxY
32580         };
32581       
32582         if ( this.isFitWidth ) {
32583             size.width = this._getContainerFitWidth();
32584         }
32585       
32586         return size;
32587     },
32588     
32589     _getContainerFitWidth : function()
32590     {
32591         var unusedCols = 0;
32592         // count unused columns
32593         var i = this.cols;
32594         while ( --i ) {
32595           if ( this.colYs[i] !== 0 ) {
32596             break;
32597           }
32598           unusedCols++;
32599         }
32600         // fit container to columns that have been used
32601         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32602     },
32603     
32604     needsResizeLayout : function()
32605     {
32606         var previousWidth = this.containerWidth;
32607         this.getContainerWidth();
32608         return previousWidth !== this.containerWidth;
32609     }
32610  
32611 });
32612
32613  
32614
32615  /*
32616  * - LGPL
32617  *
32618  * element
32619  * 
32620  */
32621
32622 /**
32623  * @class Roo.bootstrap.MasonryBrick
32624  * @extends Roo.bootstrap.Component
32625  * Bootstrap MasonryBrick class
32626  * 
32627  * @constructor
32628  * Create a new MasonryBrick
32629  * @param {Object} config The config object
32630  */
32631
32632 Roo.bootstrap.MasonryBrick = function(config){
32633     
32634     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32635     
32636     Roo.bootstrap.MasonryBrick.register(this);
32637     
32638     this.addEvents({
32639         // raw events
32640         /**
32641          * @event click
32642          * When a MasonryBrick is clcik
32643          * @param {Roo.bootstrap.MasonryBrick} this
32644          * @param {Roo.EventObject} e
32645          */
32646         "click" : true
32647     });
32648 };
32649
32650 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32651     
32652     /**
32653      * @cfg {String} title
32654      */   
32655     title : '',
32656     /**
32657      * @cfg {String} html
32658      */   
32659     html : '',
32660     /**
32661      * @cfg {String} bgimage
32662      */   
32663     bgimage : '',
32664     /**
32665      * @cfg {String} videourl
32666      */   
32667     videourl : '',
32668     /**
32669      * @cfg {String} cls
32670      */   
32671     cls : '',
32672     /**
32673      * @cfg {String} href
32674      */   
32675     href : '',
32676     /**
32677      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32678      */   
32679     size : 'xs',
32680     
32681     /**
32682      * @cfg {String} placetitle (center|bottom)
32683      */   
32684     placetitle : '',
32685     
32686     /**
32687      * @cfg {Boolean} isFitContainer defalut true
32688      */   
32689     isFitContainer : true, 
32690     
32691     /**
32692      * @cfg {Boolean} preventDefault defalut false
32693      */   
32694     preventDefault : false, 
32695     
32696     /**
32697      * @cfg {Boolean} inverse defalut false
32698      */   
32699     maskInverse : false, 
32700     
32701     getAutoCreate : function()
32702     {
32703         if(!this.isFitContainer){
32704             return this.getSplitAutoCreate();
32705         }
32706         
32707         var cls = 'masonry-brick masonry-brick-full';
32708         
32709         if(this.href.length){
32710             cls += ' masonry-brick-link';
32711         }
32712         
32713         if(this.bgimage.length){
32714             cls += ' masonry-brick-image';
32715         }
32716         
32717         if(this.maskInverse){
32718             cls += ' mask-inverse';
32719         }
32720         
32721         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32722             cls += ' enable-mask';
32723         }
32724         
32725         if(this.size){
32726             cls += ' masonry-' + this.size + '-brick';
32727         }
32728         
32729         if(this.placetitle.length){
32730             
32731             switch (this.placetitle) {
32732                 case 'center' :
32733                     cls += ' masonry-center-title';
32734                     break;
32735                 case 'bottom' :
32736                     cls += ' masonry-bottom-title';
32737                     break;
32738                 default:
32739                     break;
32740             }
32741             
32742         } else {
32743             if(!this.html.length && !this.bgimage.length){
32744                 cls += ' masonry-center-title';
32745             }
32746
32747             if(!this.html.length && this.bgimage.length){
32748                 cls += ' masonry-bottom-title';
32749             }
32750         }
32751         
32752         if(this.cls){
32753             cls += ' ' + this.cls;
32754         }
32755         
32756         var cfg = {
32757             tag: (this.href.length) ? 'a' : 'div',
32758             cls: cls,
32759             cn: [
32760                 {
32761                     tag: 'div',
32762                     cls: 'masonry-brick-mask'
32763                 },
32764                 {
32765                     tag: 'div',
32766                     cls: 'masonry-brick-paragraph',
32767                     cn: []
32768                 }
32769             ]
32770         };
32771         
32772         if(this.href.length){
32773             cfg.href = this.href;
32774         }
32775         
32776         var cn = cfg.cn[1].cn;
32777         
32778         if(this.title.length){
32779             cn.push({
32780                 tag: 'h4',
32781                 cls: 'masonry-brick-title',
32782                 html: this.title
32783             });
32784         }
32785         
32786         if(this.html.length){
32787             cn.push({
32788                 tag: 'p',
32789                 cls: 'masonry-brick-text',
32790                 html: this.html
32791             });
32792         }
32793         
32794         if (!this.title.length && !this.html.length) {
32795             cfg.cn[1].cls += ' hide';
32796         }
32797         
32798         if(this.bgimage.length){
32799             cfg.cn.push({
32800                 tag: 'img',
32801                 cls: 'masonry-brick-image-view',
32802                 src: this.bgimage
32803             });
32804         }
32805         
32806         if(this.videourl.length){
32807             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32808             // youtube support only?
32809             cfg.cn.push({
32810                 tag: 'iframe',
32811                 cls: 'masonry-brick-image-view',
32812                 src: vurl,
32813                 frameborder : 0,
32814                 allowfullscreen : true
32815             });
32816         }
32817         
32818         return cfg;
32819         
32820     },
32821     
32822     getSplitAutoCreate : function()
32823     {
32824         var cls = 'masonry-brick masonry-brick-split';
32825         
32826         if(this.href.length){
32827             cls += ' masonry-brick-link';
32828         }
32829         
32830         if(this.bgimage.length){
32831             cls += ' masonry-brick-image';
32832         }
32833         
32834         if(this.size){
32835             cls += ' masonry-' + this.size + '-brick';
32836         }
32837         
32838         switch (this.placetitle) {
32839             case 'center' :
32840                 cls += ' masonry-center-title';
32841                 break;
32842             case 'bottom' :
32843                 cls += ' masonry-bottom-title';
32844                 break;
32845             default:
32846                 if(!this.bgimage.length){
32847                     cls += ' masonry-center-title';
32848                 }
32849
32850                 if(this.bgimage.length){
32851                     cls += ' masonry-bottom-title';
32852                 }
32853                 break;
32854         }
32855         
32856         if(this.cls){
32857             cls += ' ' + this.cls;
32858         }
32859         
32860         var cfg = {
32861             tag: (this.href.length) ? 'a' : 'div',
32862             cls: cls,
32863             cn: [
32864                 {
32865                     tag: 'div',
32866                     cls: 'masonry-brick-split-head',
32867                     cn: [
32868                         {
32869                             tag: 'div',
32870                             cls: 'masonry-brick-paragraph',
32871                             cn: []
32872                         }
32873                     ]
32874                 },
32875                 {
32876                     tag: 'div',
32877                     cls: 'masonry-brick-split-body',
32878                     cn: []
32879                 }
32880             ]
32881         };
32882         
32883         if(this.href.length){
32884             cfg.href = this.href;
32885         }
32886         
32887         if(this.title.length){
32888             cfg.cn[0].cn[0].cn.push({
32889                 tag: 'h4',
32890                 cls: 'masonry-brick-title',
32891                 html: this.title
32892             });
32893         }
32894         
32895         if(this.html.length){
32896             cfg.cn[1].cn.push({
32897                 tag: 'p',
32898                 cls: 'masonry-brick-text',
32899                 html: this.html
32900             });
32901         }
32902
32903         if(this.bgimage.length){
32904             cfg.cn[0].cn.push({
32905                 tag: 'img',
32906                 cls: 'masonry-brick-image-view',
32907                 src: this.bgimage
32908             });
32909         }
32910         
32911         if(this.videourl.length){
32912             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32913             // youtube support only?
32914             cfg.cn[0].cn.cn.push({
32915                 tag: 'iframe',
32916                 cls: 'masonry-brick-image-view',
32917                 src: vurl,
32918                 frameborder : 0,
32919                 allowfullscreen : true
32920             });
32921         }
32922         
32923         return cfg;
32924     },
32925     
32926     initEvents: function() 
32927     {
32928         switch (this.size) {
32929             case 'xs' :
32930                 this.x = 1;
32931                 this.y = 1;
32932                 break;
32933             case 'sm' :
32934                 this.x = 2;
32935                 this.y = 2;
32936                 break;
32937             case 'md' :
32938             case 'md-left' :
32939             case 'md-right' :
32940                 this.x = 3;
32941                 this.y = 3;
32942                 break;
32943             case 'tall' :
32944                 this.x = 2;
32945                 this.y = 3;
32946                 break;
32947             case 'wide' :
32948                 this.x = 3;
32949                 this.y = 2;
32950                 break;
32951             case 'wide-thin' :
32952                 this.x = 3;
32953                 this.y = 1;
32954                 break;
32955                         
32956             default :
32957                 break;
32958         }
32959         
32960         if(Roo.isTouch){
32961             this.el.on('touchstart', this.onTouchStart, this);
32962             this.el.on('touchmove', this.onTouchMove, this);
32963             this.el.on('touchend', this.onTouchEnd, this);
32964             this.el.on('contextmenu', this.onContextMenu, this);
32965         } else {
32966             this.el.on('mouseenter'  ,this.enter, this);
32967             this.el.on('mouseleave', this.leave, this);
32968             this.el.on('click', this.onClick, this);
32969         }
32970         
32971         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32972             this.parent().bricks.push(this);   
32973         }
32974         
32975     },
32976     
32977     onClick: function(e, el)
32978     {
32979         var time = this.endTimer - this.startTimer;
32980         // Roo.log(e.preventDefault());
32981         if(Roo.isTouch){
32982             if(time > 1000){
32983                 e.preventDefault();
32984                 return;
32985             }
32986         }
32987         
32988         if(!this.preventDefault){
32989             return;
32990         }
32991         
32992         e.preventDefault();
32993         
32994         if (this.activeClass != '') {
32995             this.selectBrick();
32996         }
32997         
32998         this.fireEvent('click', this, e);
32999     },
33000     
33001     enter: function(e, el)
33002     {
33003         e.preventDefault();
33004         
33005         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33006             return;
33007         }
33008         
33009         if(this.bgimage.length && this.html.length){
33010             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33011         }
33012     },
33013     
33014     leave: function(e, el)
33015     {
33016         e.preventDefault();
33017         
33018         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33019             return;
33020         }
33021         
33022         if(this.bgimage.length && this.html.length){
33023             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33024         }
33025     },
33026     
33027     onTouchStart: function(e, el)
33028     {
33029 //        e.preventDefault();
33030         
33031         this.touchmoved = false;
33032         
33033         if(!this.isFitContainer){
33034             return;
33035         }
33036         
33037         if(!this.bgimage.length || !this.html.length){
33038             return;
33039         }
33040         
33041         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33042         
33043         this.timer = new Date().getTime();
33044         
33045     },
33046     
33047     onTouchMove: function(e, el)
33048     {
33049         this.touchmoved = true;
33050     },
33051     
33052     onContextMenu : function(e,el)
33053     {
33054         e.preventDefault();
33055         e.stopPropagation();
33056         return false;
33057     },
33058     
33059     onTouchEnd: function(e, el)
33060     {
33061 //        e.preventDefault();
33062         
33063         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33064         
33065             this.leave(e,el);
33066             
33067             return;
33068         }
33069         
33070         if(!this.bgimage.length || !this.html.length){
33071             
33072             if(this.href.length){
33073                 window.location.href = this.href;
33074             }
33075             
33076             return;
33077         }
33078         
33079         if(!this.isFitContainer){
33080             return;
33081         }
33082         
33083         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33084         
33085         window.location.href = this.href;
33086     },
33087     
33088     //selection on single brick only
33089     selectBrick : function() {
33090         
33091         if (!this.parentId) {
33092             return;
33093         }
33094         
33095         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33096         var index = m.selectedBrick.indexOf(this.id);
33097         
33098         if ( index > -1) {
33099             m.selectedBrick.splice(index,1);
33100             this.el.removeClass(this.activeClass);
33101             return;
33102         }
33103         
33104         for(var i = 0; i < m.selectedBrick.length; i++) {
33105             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33106             b.el.removeClass(b.activeClass);
33107         }
33108         
33109         m.selectedBrick = [];
33110         
33111         m.selectedBrick.push(this.id);
33112         this.el.addClass(this.activeClass);
33113         return;
33114     },
33115     
33116     isSelected : function(){
33117         return this.el.hasClass(this.activeClass);
33118         
33119     }
33120 });
33121
33122 Roo.apply(Roo.bootstrap.MasonryBrick, {
33123     
33124     //groups: {},
33125     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33126      /**
33127     * register a Masonry Brick
33128     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33129     */
33130     
33131     register : function(brick)
33132     {
33133         //this.groups[brick.id] = brick;
33134         this.groups.add(brick.id, brick);
33135     },
33136     /**
33137     * fetch a  masonry brick based on the masonry brick ID
33138     * @param {string} the masonry brick to add
33139     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33140     */
33141     
33142     get: function(brick_id) 
33143     {
33144         // if (typeof(this.groups[brick_id]) == 'undefined') {
33145         //     return false;
33146         // }
33147         // return this.groups[brick_id] ;
33148         
33149         if(this.groups.key(brick_id)) {
33150             return this.groups.key(brick_id);
33151         }
33152         
33153         return false;
33154     }
33155     
33156     
33157     
33158 });
33159
33160  /*
33161  * - LGPL
33162  *
33163  * element
33164  * 
33165  */
33166
33167 /**
33168  * @class Roo.bootstrap.Brick
33169  * @extends Roo.bootstrap.Component
33170  * Bootstrap Brick class
33171  * 
33172  * @constructor
33173  * Create a new Brick
33174  * @param {Object} config The config object
33175  */
33176
33177 Roo.bootstrap.Brick = function(config){
33178     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33179     
33180     this.addEvents({
33181         // raw events
33182         /**
33183          * @event click
33184          * When a Brick is click
33185          * @param {Roo.bootstrap.Brick} this
33186          * @param {Roo.EventObject} e
33187          */
33188         "click" : true
33189     });
33190 };
33191
33192 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33193     
33194     /**
33195      * @cfg {String} title
33196      */   
33197     title : '',
33198     /**
33199      * @cfg {String} html
33200      */   
33201     html : '',
33202     /**
33203      * @cfg {String} bgimage
33204      */   
33205     bgimage : '',
33206     /**
33207      * @cfg {String} cls
33208      */   
33209     cls : '',
33210     /**
33211      * @cfg {String} href
33212      */   
33213     href : '',
33214     /**
33215      * @cfg {String} video
33216      */   
33217     video : '',
33218     /**
33219      * @cfg {Boolean} square
33220      */   
33221     square : true,
33222     
33223     getAutoCreate : function()
33224     {
33225         var cls = 'roo-brick';
33226         
33227         if(this.href.length){
33228             cls += ' roo-brick-link';
33229         }
33230         
33231         if(this.bgimage.length){
33232             cls += ' roo-brick-image';
33233         }
33234         
33235         if(!this.html.length && !this.bgimage.length){
33236             cls += ' roo-brick-center-title';
33237         }
33238         
33239         if(!this.html.length && this.bgimage.length){
33240             cls += ' roo-brick-bottom-title';
33241         }
33242         
33243         if(this.cls){
33244             cls += ' ' + this.cls;
33245         }
33246         
33247         var cfg = {
33248             tag: (this.href.length) ? 'a' : 'div',
33249             cls: cls,
33250             cn: [
33251                 {
33252                     tag: 'div',
33253                     cls: 'roo-brick-paragraph',
33254                     cn: []
33255                 }
33256             ]
33257         };
33258         
33259         if(this.href.length){
33260             cfg.href = this.href;
33261         }
33262         
33263         var cn = cfg.cn[0].cn;
33264         
33265         if(this.title.length){
33266             cn.push({
33267                 tag: 'h4',
33268                 cls: 'roo-brick-title',
33269                 html: this.title
33270             });
33271         }
33272         
33273         if(this.html.length){
33274             cn.push({
33275                 tag: 'p',
33276                 cls: 'roo-brick-text',
33277                 html: this.html
33278             });
33279         } else {
33280             cn.cls += ' hide';
33281         }
33282         
33283         if(this.bgimage.length){
33284             cfg.cn.push({
33285                 tag: 'img',
33286                 cls: 'roo-brick-image-view',
33287                 src: this.bgimage
33288             });
33289         }
33290         
33291         return cfg;
33292     },
33293     
33294     initEvents: function() 
33295     {
33296         if(this.title.length || this.html.length){
33297             this.el.on('mouseenter'  ,this.enter, this);
33298             this.el.on('mouseleave', this.leave, this);
33299         }
33300         
33301         Roo.EventManager.onWindowResize(this.resize, this); 
33302         
33303         if(this.bgimage.length){
33304             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33305             this.imageEl.on('load', this.onImageLoad, this);
33306             return;
33307         }
33308         
33309         this.resize();
33310     },
33311     
33312     onImageLoad : function()
33313     {
33314         this.resize();
33315     },
33316     
33317     resize : function()
33318     {
33319         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33320         
33321         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33322         
33323         if(this.bgimage.length){
33324             var image = this.el.select('.roo-brick-image-view', true).first();
33325             
33326             image.setWidth(paragraph.getWidth());
33327             
33328             if(this.square){
33329                 image.setHeight(paragraph.getWidth());
33330             }
33331             
33332             this.el.setHeight(image.getHeight());
33333             paragraph.setHeight(image.getHeight());
33334             
33335         }
33336         
33337     },
33338     
33339     enter: function(e, el)
33340     {
33341         e.preventDefault();
33342         
33343         if(this.bgimage.length){
33344             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33345             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33346         }
33347     },
33348     
33349     leave: function(e, el)
33350     {
33351         e.preventDefault();
33352         
33353         if(this.bgimage.length){
33354             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33355             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33356         }
33357     }
33358     
33359 });
33360
33361  
33362
33363  /*
33364  * - LGPL
33365  *
33366  * Number field 
33367  */
33368
33369 /**
33370  * @class Roo.bootstrap.NumberField
33371  * @extends Roo.bootstrap.Input
33372  * Bootstrap NumberField class
33373  * 
33374  * 
33375  * 
33376  * 
33377  * @constructor
33378  * Create a new NumberField
33379  * @param {Object} config The config object
33380  */
33381
33382 Roo.bootstrap.NumberField = function(config){
33383     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33384 };
33385
33386 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33387     
33388     /**
33389      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33390      */
33391     allowDecimals : true,
33392     /**
33393      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33394      */
33395     decimalSeparator : ".",
33396     /**
33397      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33398      */
33399     decimalPrecision : 2,
33400     /**
33401      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33402      */
33403     allowNegative : true,
33404     
33405     /**
33406      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33407      */
33408     allowZero: true,
33409     /**
33410      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33411      */
33412     minValue : Number.NEGATIVE_INFINITY,
33413     /**
33414      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33415      */
33416     maxValue : Number.MAX_VALUE,
33417     /**
33418      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33419      */
33420     minText : "The minimum value for this field is {0}",
33421     /**
33422      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33423      */
33424     maxText : "The maximum value for this field is {0}",
33425     /**
33426      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33427      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33428      */
33429     nanText : "{0} is not a valid number",
33430     /**
33431      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33432      */
33433     thousandsDelimiter : false,
33434     /**
33435      * @cfg {String} valueAlign alignment of value
33436      */
33437     valueAlign : "left",
33438
33439     getAutoCreate : function()
33440     {
33441         var hiddenInput = {
33442             tag: 'input',
33443             type: 'hidden',
33444             id: Roo.id(),
33445             cls: 'hidden-number-input'
33446         };
33447         
33448         if (this.name) {
33449             hiddenInput.name = this.name;
33450         }
33451         
33452         this.name = '';
33453         
33454         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33455         
33456         this.name = hiddenInput.name;
33457         
33458         if(cfg.cn.length > 0) {
33459             cfg.cn.push(hiddenInput);
33460         }
33461         
33462         return cfg;
33463     },
33464
33465     // private
33466     initEvents : function()
33467     {   
33468         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33469         
33470         var allowed = "0123456789";
33471         
33472         if(this.allowDecimals){
33473             allowed += this.decimalSeparator;
33474         }
33475         
33476         if(this.allowNegative){
33477             allowed += "-";
33478         }
33479         
33480         if(this.thousandsDelimiter) {
33481             allowed += ",";
33482         }
33483         
33484         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33485         
33486         var keyPress = function(e){
33487             
33488             var k = e.getKey();
33489             
33490             var c = e.getCharCode();
33491             
33492             if(
33493                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33494                     allowed.indexOf(String.fromCharCode(c)) === -1
33495             ){
33496                 e.stopEvent();
33497                 return;
33498             }
33499             
33500             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33501                 return;
33502             }
33503             
33504             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33505                 e.stopEvent();
33506             }
33507         };
33508         
33509         this.el.on("keypress", keyPress, this);
33510     },
33511     
33512     validateValue : function(value)
33513     {
33514         
33515         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33516             return false;
33517         }
33518         
33519         var num = this.parseValue(value);
33520         
33521         if(isNaN(num)){
33522             this.markInvalid(String.format(this.nanText, value));
33523             return false;
33524         }
33525         
33526         if(num < this.minValue){
33527             this.markInvalid(String.format(this.minText, this.minValue));
33528             return false;
33529         }
33530         
33531         if(num > this.maxValue){
33532             this.markInvalid(String.format(this.maxText, this.maxValue));
33533             return false;
33534         }
33535         
33536         return true;
33537     },
33538
33539     getValue : function()
33540     {
33541         var v = this.hiddenEl().getValue();
33542         
33543         return this.fixPrecision(this.parseValue(v));
33544     },
33545
33546     parseValue : function(value)
33547     {
33548         if(this.thousandsDelimiter) {
33549             value += "";
33550             r = new RegExp(",", "g");
33551             value = value.replace(r, "");
33552         }
33553         
33554         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33555         return isNaN(value) ? '' : value;
33556     },
33557
33558     fixPrecision : function(value)
33559     {
33560         if(this.thousandsDelimiter) {
33561             value += "";
33562             r = new RegExp(",", "g");
33563             value = value.replace(r, "");
33564         }
33565         
33566         var nan = isNaN(value);
33567         
33568         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33569             return nan ? '' : value;
33570         }
33571         return parseFloat(value).toFixed(this.decimalPrecision);
33572     },
33573
33574     setValue : function(v)
33575     {
33576         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33577         
33578         this.value = v;
33579         
33580         if(this.rendered){
33581             
33582             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33583             
33584             this.inputEl().dom.value = (v == '') ? '' :
33585                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33586             
33587             if(!this.allowZero && v === '0') {
33588                 this.hiddenEl().dom.value = '';
33589                 this.inputEl().dom.value = '';
33590             }
33591             
33592             this.validate();
33593         }
33594     },
33595
33596     decimalPrecisionFcn : function(v)
33597     {
33598         return Math.floor(v);
33599     },
33600
33601     beforeBlur : function()
33602     {
33603         var v = this.parseValue(this.getRawValue());
33604         
33605         if(v || v === 0 || v === ''){
33606             this.setValue(v);
33607         }
33608     },
33609     
33610     hiddenEl : function()
33611     {
33612         return this.el.select('input.hidden-number-input',true).first();
33613     }
33614     
33615 });
33616
33617  
33618
33619 /*
33620 * Licence: LGPL
33621 */
33622
33623 /**
33624  * @class Roo.bootstrap.DocumentSlider
33625  * @extends Roo.bootstrap.Component
33626  * Bootstrap DocumentSlider class
33627  * 
33628  * @constructor
33629  * Create a new DocumentViewer
33630  * @param {Object} config The config object
33631  */
33632
33633 Roo.bootstrap.DocumentSlider = function(config){
33634     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33635     
33636     this.files = [];
33637     
33638     this.addEvents({
33639         /**
33640          * @event initial
33641          * Fire after initEvent
33642          * @param {Roo.bootstrap.DocumentSlider} this
33643          */
33644         "initial" : true,
33645         /**
33646          * @event update
33647          * Fire after update
33648          * @param {Roo.bootstrap.DocumentSlider} this
33649          */
33650         "update" : true,
33651         /**
33652          * @event click
33653          * Fire after click
33654          * @param {Roo.bootstrap.DocumentSlider} this
33655          */
33656         "click" : true
33657     });
33658 };
33659
33660 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33661     
33662     files : false,
33663     
33664     indicator : 0,
33665     
33666     getAutoCreate : function()
33667     {
33668         var cfg = {
33669             tag : 'div',
33670             cls : 'roo-document-slider',
33671             cn : [
33672                 {
33673                     tag : 'div',
33674                     cls : 'roo-document-slider-header',
33675                     cn : [
33676                         {
33677                             tag : 'div',
33678                             cls : 'roo-document-slider-header-title'
33679                         }
33680                     ]
33681                 },
33682                 {
33683                     tag : 'div',
33684                     cls : 'roo-document-slider-body',
33685                     cn : [
33686                         {
33687                             tag : 'div',
33688                             cls : 'roo-document-slider-prev',
33689                             cn : [
33690                                 {
33691                                     tag : 'i',
33692                                     cls : 'fa fa-chevron-left'
33693                                 }
33694                             ]
33695                         },
33696                         {
33697                             tag : 'div',
33698                             cls : 'roo-document-slider-thumb',
33699                             cn : [
33700                                 {
33701                                     tag : 'img',
33702                                     cls : 'roo-document-slider-image'
33703                                 }
33704                             ]
33705                         },
33706                         {
33707                             tag : 'div',
33708                             cls : 'roo-document-slider-next',
33709                             cn : [
33710                                 {
33711                                     tag : 'i',
33712                                     cls : 'fa fa-chevron-right'
33713                                 }
33714                             ]
33715                         }
33716                     ]
33717                 }
33718             ]
33719         };
33720         
33721         return cfg;
33722     },
33723     
33724     initEvents : function()
33725     {
33726         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33727         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33728         
33729         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33730         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33731         
33732         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33733         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33734         
33735         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33736         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33737         
33738         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33739         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33740         
33741         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33742         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33743         
33744         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33745         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33746         
33747         this.thumbEl.on('click', this.onClick, this);
33748         
33749         this.prevIndicator.on('click', this.prev, this);
33750         
33751         this.nextIndicator.on('click', this.next, this);
33752         
33753     },
33754     
33755     initial : function()
33756     {
33757         if(this.files.length){
33758             this.indicator = 1;
33759             this.update()
33760         }
33761         
33762         this.fireEvent('initial', this);
33763     },
33764     
33765     update : function()
33766     {
33767         this.imageEl.attr('src', this.files[this.indicator - 1]);
33768         
33769         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33770         
33771         this.prevIndicator.show();
33772         
33773         if(this.indicator == 1){
33774             this.prevIndicator.hide();
33775         }
33776         
33777         this.nextIndicator.show();
33778         
33779         if(this.indicator == this.files.length){
33780             this.nextIndicator.hide();
33781         }
33782         
33783         this.thumbEl.scrollTo('top');
33784         
33785         this.fireEvent('update', this);
33786     },
33787     
33788     onClick : function(e)
33789     {
33790         e.preventDefault();
33791         
33792         this.fireEvent('click', this);
33793     },
33794     
33795     prev : function(e)
33796     {
33797         e.preventDefault();
33798         
33799         this.indicator = Math.max(1, this.indicator - 1);
33800         
33801         this.update();
33802     },
33803     
33804     next : function(e)
33805     {
33806         e.preventDefault();
33807         
33808         this.indicator = Math.min(this.files.length, this.indicator + 1);
33809         
33810         this.update();
33811     }
33812 });
33813 /*
33814  * - LGPL
33815  *
33816  * RadioSet
33817  *
33818  *
33819  */
33820
33821 /**
33822  * @class Roo.bootstrap.RadioSet
33823  * @extends Roo.bootstrap.Input
33824  * Bootstrap RadioSet class
33825  * @cfg {String} indicatorpos (left|right) default left
33826  * @cfg {Boolean} inline (true|false) inline the element (default true)
33827  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33828  * @constructor
33829  * Create a new RadioSet
33830  * @param {Object} config The config object
33831  */
33832
33833 Roo.bootstrap.RadioSet = function(config){
33834     
33835     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33836     
33837     this.radioes = [];
33838     
33839     Roo.bootstrap.RadioSet.register(this);
33840     
33841     this.addEvents({
33842         /**
33843         * @event check
33844         * Fires when the element is checked or unchecked.
33845         * @param {Roo.bootstrap.RadioSet} this This radio
33846         * @param {Roo.bootstrap.Radio} item The checked item
33847         */
33848        check : true,
33849        /**
33850         * @event click
33851         * Fires when the element is click.
33852         * @param {Roo.bootstrap.RadioSet} this This radio set
33853         * @param {Roo.bootstrap.Radio} item The checked item
33854         * @param {Roo.EventObject} e The event object
33855         */
33856        click : true
33857     });
33858     
33859 };
33860
33861 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33862
33863     radioes : false,
33864     
33865     inline : true,
33866     
33867     weight : '',
33868     
33869     indicatorpos : 'left',
33870     
33871     getAutoCreate : function()
33872     {
33873         var label = {
33874             tag : 'label',
33875             cls : 'roo-radio-set-label',
33876             cn : [
33877                 {
33878                     tag : 'span',
33879                     html : this.fieldLabel
33880                 }
33881             ]
33882         };
33883         
33884         if(this.indicatorpos == 'left'){
33885             label.cn.unshift({
33886                 tag : 'i',
33887                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33888                 tooltip : 'This field is required'
33889             });
33890         } else {
33891             label.cn.push({
33892                 tag : 'i',
33893                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33894                 tooltip : 'This field is required'
33895             });
33896         }
33897         
33898         var items = {
33899             tag : 'div',
33900             cls : 'roo-radio-set-items'
33901         };
33902         
33903         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33904         
33905         if (align === 'left' && this.fieldLabel.length) {
33906             
33907             items = {
33908                 cls : "roo-radio-set-right", 
33909                 cn: [
33910                     items
33911                 ]
33912             };
33913             
33914             if(this.labelWidth > 12){
33915                 label.style = "width: " + this.labelWidth + 'px';
33916             }
33917             
33918             if(this.labelWidth < 13 && this.labelmd == 0){
33919                 this.labelmd = this.labelWidth;
33920             }
33921             
33922             if(this.labellg > 0){
33923                 label.cls += ' col-lg-' + this.labellg;
33924                 items.cls += ' col-lg-' + (12 - this.labellg);
33925             }
33926             
33927             if(this.labelmd > 0){
33928                 label.cls += ' col-md-' + this.labelmd;
33929                 items.cls += ' col-md-' + (12 - this.labelmd);
33930             }
33931             
33932             if(this.labelsm > 0){
33933                 label.cls += ' col-sm-' + this.labelsm;
33934                 items.cls += ' col-sm-' + (12 - this.labelsm);
33935             }
33936             
33937             if(this.labelxs > 0){
33938                 label.cls += ' col-xs-' + this.labelxs;
33939                 items.cls += ' col-xs-' + (12 - this.labelxs);
33940             }
33941         }
33942         
33943         var cfg = {
33944             tag : 'div',
33945             cls : 'roo-radio-set',
33946             cn : [
33947                 {
33948                     tag : 'input',
33949                     cls : 'roo-radio-set-input',
33950                     type : 'hidden',
33951                     name : this.name,
33952                     value : this.value ? this.value :  ''
33953                 },
33954                 label,
33955                 items
33956             ]
33957         };
33958         
33959         if(this.weight.length){
33960             cfg.cls += ' roo-radio-' + this.weight;
33961         }
33962         
33963         if(this.inline) {
33964             cfg.cls += ' roo-radio-set-inline';
33965         }
33966         
33967         var settings=this;
33968         ['xs','sm','md','lg'].map(function(size){
33969             if (settings[size]) {
33970                 cfg.cls += ' col-' + size + '-' + settings[size];
33971             }
33972         });
33973         
33974         return cfg;
33975         
33976     },
33977
33978     initEvents : function()
33979     {
33980         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33981         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33982         
33983         if(!this.fieldLabel.length){
33984             this.labelEl.hide();
33985         }
33986         
33987         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33988         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33989         
33990         this.indicator = this.indicatorEl();
33991         
33992         if(this.indicator){
33993             this.indicator.addClass('invisible');
33994         }
33995         
33996         this.originalValue = this.getValue();
33997         
33998     },
33999     
34000     inputEl: function ()
34001     {
34002         return this.el.select('.roo-radio-set-input', true).first();
34003     },
34004     
34005     getChildContainer : function()
34006     {
34007         return this.itemsEl;
34008     },
34009     
34010     register : function(item)
34011     {
34012         this.radioes.push(item);
34013         
34014     },
34015     
34016     validate : function()
34017     {   
34018         if(this.getVisibilityEl().hasClass('hidden')){
34019             return true;
34020         }
34021         
34022         var valid = false;
34023         
34024         Roo.each(this.radioes, function(i){
34025             if(!i.checked){
34026                 return;
34027             }
34028             
34029             valid = true;
34030             return false;
34031         });
34032         
34033         if(this.allowBlank) {
34034             return true;
34035         }
34036         
34037         if(this.disabled || valid){
34038             this.markValid();
34039             return true;
34040         }
34041         
34042         this.markInvalid();
34043         return false;
34044         
34045     },
34046     
34047     markValid : function()
34048     {
34049         if(this.labelEl.isVisible(true)){
34050             this.indicatorEl().removeClass('visible');
34051             this.indicatorEl().addClass('invisible');
34052         }
34053         
34054         this.el.removeClass([this.invalidClass, this.validClass]);
34055         this.el.addClass(this.validClass);
34056         
34057         this.fireEvent('valid', this);
34058     },
34059     
34060     markInvalid : function(msg)
34061     {
34062         if(this.allowBlank || this.disabled){
34063             return;
34064         }
34065         
34066         if(this.labelEl.isVisible(true)){
34067             this.indicatorEl().removeClass('invisible');
34068             this.indicatorEl().addClass('visible');
34069         }
34070         
34071         this.el.removeClass([this.invalidClass, this.validClass]);
34072         this.el.addClass(this.invalidClass);
34073         
34074         this.fireEvent('invalid', this, msg);
34075         
34076     },
34077     
34078     setValue : function(v, suppressEvent)
34079     {   
34080         if(this.value === v){
34081             return;
34082         }
34083         
34084         this.value = v;
34085         
34086         if(this.rendered){
34087             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34088         }
34089         
34090         Roo.each(this.radioes, function(i){
34091             i.checked = false;
34092             i.el.removeClass('checked');
34093         });
34094         
34095         Roo.each(this.radioes, function(i){
34096             
34097             if(i.value === v || i.value.toString() === v.toString()){
34098                 i.checked = true;
34099                 i.el.addClass('checked');
34100                 
34101                 if(suppressEvent !== true){
34102                     this.fireEvent('check', this, i);
34103                 }
34104                 
34105                 return false;
34106             }
34107             
34108         }, this);
34109         
34110         this.validate();
34111     },
34112     
34113     clearInvalid : function(){
34114         
34115         if(!this.el || this.preventMark){
34116             return;
34117         }
34118         
34119         this.el.removeClass([this.invalidClass]);
34120         
34121         this.fireEvent('valid', this);
34122     }
34123     
34124 });
34125
34126 Roo.apply(Roo.bootstrap.RadioSet, {
34127     
34128     groups: {},
34129     
34130     register : function(set)
34131     {
34132         this.groups[set.name] = set;
34133     },
34134     
34135     get: function(name) 
34136     {
34137         if (typeof(this.groups[name]) == 'undefined') {
34138             return false;
34139         }
34140         
34141         return this.groups[name] ;
34142     }
34143     
34144 });
34145 /*
34146  * Based on:
34147  * Ext JS Library 1.1.1
34148  * Copyright(c) 2006-2007, Ext JS, LLC.
34149  *
34150  * Originally Released Under LGPL - original licence link has changed is not relivant.
34151  *
34152  * Fork - LGPL
34153  * <script type="text/javascript">
34154  */
34155
34156
34157 /**
34158  * @class Roo.bootstrap.SplitBar
34159  * @extends Roo.util.Observable
34160  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34161  * <br><br>
34162  * Usage:
34163  * <pre><code>
34164 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34165                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34166 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34167 split.minSize = 100;
34168 split.maxSize = 600;
34169 split.animate = true;
34170 split.on('moved', splitterMoved);
34171 </code></pre>
34172  * @constructor
34173  * Create a new SplitBar
34174  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34175  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34176  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34177  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34178                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34179                         position of the SplitBar).
34180  */
34181 Roo.bootstrap.SplitBar = function(cfg){
34182     
34183     /** @private */
34184     
34185     //{
34186     //  dragElement : elm
34187     //  resizingElement: el,
34188         // optional..
34189     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34190     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34191         // existingProxy ???
34192     //}
34193     
34194     this.el = Roo.get(cfg.dragElement, true);
34195     this.el.dom.unselectable = "on";
34196     /** @private */
34197     this.resizingEl = Roo.get(cfg.resizingElement, true);
34198
34199     /**
34200      * @private
34201      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34202      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34203      * @type Number
34204      */
34205     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34206     
34207     /**
34208      * The minimum size of the resizing element. (Defaults to 0)
34209      * @type Number
34210      */
34211     this.minSize = 0;
34212     
34213     /**
34214      * The maximum size of the resizing element. (Defaults to 2000)
34215      * @type Number
34216      */
34217     this.maxSize = 2000;
34218     
34219     /**
34220      * Whether to animate the transition to the new size
34221      * @type Boolean
34222      */
34223     this.animate = false;
34224     
34225     /**
34226      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34227      * @type Boolean
34228      */
34229     this.useShim = false;
34230     
34231     /** @private */
34232     this.shim = null;
34233     
34234     if(!cfg.existingProxy){
34235         /** @private */
34236         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34237     }else{
34238         this.proxy = Roo.get(cfg.existingProxy).dom;
34239     }
34240     /** @private */
34241     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34242     
34243     /** @private */
34244     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34245     
34246     /** @private */
34247     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34248     
34249     /** @private */
34250     this.dragSpecs = {};
34251     
34252     /**
34253      * @private The adapter to use to positon and resize elements
34254      */
34255     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34256     this.adapter.init(this);
34257     
34258     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34259         /** @private */
34260         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34261         this.el.addClass("roo-splitbar-h");
34262     }else{
34263         /** @private */
34264         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34265         this.el.addClass("roo-splitbar-v");
34266     }
34267     
34268     this.addEvents({
34269         /**
34270          * @event resize
34271          * Fires when the splitter is moved (alias for {@link #event-moved})
34272          * @param {Roo.bootstrap.SplitBar} this
34273          * @param {Number} newSize the new width or height
34274          */
34275         "resize" : true,
34276         /**
34277          * @event moved
34278          * Fires when the splitter is moved
34279          * @param {Roo.bootstrap.SplitBar} this
34280          * @param {Number} newSize the new width or height
34281          */
34282         "moved" : true,
34283         /**
34284          * @event beforeresize
34285          * Fires before the splitter is dragged
34286          * @param {Roo.bootstrap.SplitBar} this
34287          */
34288         "beforeresize" : true,
34289
34290         "beforeapply" : true
34291     });
34292
34293     Roo.util.Observable.call(this);
34294 };
34295
34296 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34297     onStartProxyDrag : function(x, y){
34298         this.fireEvent("beforeresize", this);
34299         if(!this.overlay){
34300             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34301             o.unselectable();
34302             o.enableDisplayMode("block");
34303             // all splitbars share the same overlay
34304             Roo.bootstrap.SplitBar.prototype.overlay = o;
34305         }
34306         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34307         this.overlay.show();
34308         Roo.get(this.proxy).setDisplayed("block");
34309         var size = this.adapter.getElementSize(this);
34310         this.activeMinSize = this.getMinimumSize();;
34311         this.activeMaxSize = this.getMaximumSize();;
34312         var c1 = size - this.activeMinSize;
34313         var c2 = Math.max(this.activeMaxSize - size, 0);
34314         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34315             this.dd.resetConstraints();
34316             this.dd.setXConstraint(
34317                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34318                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34319             );
34320             this.dd.setYConstraint(0, 0);
34321         }else{
34322             this.dd.resetConstraints();
34323             this.dd.setXConstraint(0, 0);
34324             this.dd.setYConstraint(
34325                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34326                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34327             );
34328          }
34329         this.dragSpecs.startSize = size;
34330         this.dragSpecs.startPoint = [x, y];
34331         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34332     },
34333     
34334     /** 
34335      * @private Called after the drag operation by the DDProxy
34336      */
34337     onEndProxyDrag : function(e){
34338         Roo.get(this.proxy).setDisplayed(false);
34339         var endPoint = Roo.lib.Event.getXY(e);
34340         if(this.overlay){
34341             this.overlay.hide();
34342         }
34343         var newSize;
34344         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34345             newSize = this.dragSpecs.startSize + 
34346                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34347                     endPoint[0] - this.dragSpecs.startPoint[0] :
34348                     this.dragSpecs.startPoint[0] - endPoint[0]
34349                 );
34350         }else{
34351             newSize = this.dragSpecs.startSize + 
34352                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34353                     endPoint[1] - this.dragSpecs.startPoint[1] :
34354                     this.dragSpecs.startPoint[1] - endPoint[1]
34355                 );
34356         }
34357         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34358         if(newSize != this.dragSpecs.startSize){
34359             if(this.fireEvent('beforeapply', this, newSize) !== false){
34360                 this.adapter.setElementSize(this, newSize);
34361                 this.fireEvent("moved", this, newSize);
34362                 this.fireEvent("resize", this, newSize);
34363             }
34364         }
34365     },
34366     
34367     /**
34368      * Get the adapter this SplitBar uses
34369      * @return The adapter object
34370      */
34371     getAdapter : function(){
34372         return this.adapter;
34373     },
34374     
34375     /**
34376      * Set the adapter this SplitBar uses
34377      * @param {Object} adapter A SplitBar adapter object
34378      */
34379     setAdapter : function(adapter){
34380         this.adapter = adapter;
34381         this.adapter.init(this);
34382     },
34383     
34384     /**
34385      * Gets the minimum size for the resizing element
34386      * @return {Number} The minimum size
34387      */
34388     getMinimumSize : function(){
34389         return this.minSize;
34390     },
34391     
34392     /**
34393      * Sets the minimum size for the resizing element
34394      * @param {Number} minSize The minimum size
34395      */
34396     setMinimumSize : function(minSize){
34397         this.minSize = minSize;
34398     },
34399     
34400     /**
34401      * Gets the maximum size for the resizing element
34402      * @return {Number} The maximum size
34403      */
34404     getMaximumSize : function(){
34405         return this.maxSize;
34406     },
34407     
34408     /**
34409      * Sets the maximum size for the resizing element
34410      * @param {Number} maxSize The maximum size
34411      */
34412     setMaximumSize : function(maxSize){
34413         this.maxSize = maxSize;
34414     },
34415     
34416     /**
34417      * Sets the initialize size for the resizing element
34418      * @param {Number} size The initial size
34419      */
34420     setCurrentSize : function(size){
34421         var oldAnimate = this.animate;
34422         this.animate = false;
34423         this.adapter.setElementSize(this, size);
34424         this.animate = oldAnimate;
34425     },
34426     
34427     /**
34428      * Destroy this splitbar. 
34429      * @param {Boolean} removeEl True to remove the element
34430      */
34431     destroy : function(removeEl){
34432         if(this.shim){
34433             this.shim.remove();
34434         }
34435         this.dd.unreg();
34436         this.proxy.parentNode.removeChild(this.proxy);
34437         if(removeEl){
34438             this.el.remove();
34439         }
34440     }
34441 });
34442
34443 /**
34444  * @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.
34445  */
34446 Roo.bootstrap.SplitBar.createProxy = function(dir){
34447     var proxy = new Roo.Element(document.createElement("div"));
34448     proxy.unselectable();
34449     var cls = 'roo-splitbar-proxy';
34450     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34451     document.body.appendChild(proxy.dom);
34452     return proxy.dom;
34453 };
34454
34455 /** 
34456  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34457  * Default Adapter. It assumes the splitter and resizing element are not positioned
34458  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34459  */
34460 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34461 };
34462
34463 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34464     // do nothing for now
34465     init : function(s){
34466     
34467     },
34468     /**
34469      * Called before drag operations to get the current size of the resizing element. 
34470      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34471      */
34472      getElementSize : function(s){
34473         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34474             return s.resizingEl.getWidth();
34475         }else{
34476             return s.resizingEl.getHeight();
34477         }
34478     },
34479     
34480     /**
34481      * Called after drag operations to set the size of the resizing element.
34482      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34483      * @param {Number} newSize The new size to set
34484      * @param {Function} onComplete A function to be invoked when resizing is complete
34485      */
34486     setElementSize : function(s, newSize, onComplete){
34487         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34488             if(!s.animate){
34489                 s.resizingEl.setWidth(newSize);
34490                 if(onComplete){
34491                     onComplete(s, newSize);
34492                 }
34493             }else{
34494                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34495             }
34496         }else{
34497             
34498             if(!s.animate){
34499                 s.resizingEl.setHeight(newSize);
34500                 if(onComplete){
34501                     onComplete(s, newSize);
34502                 }
34503             }else{
34504                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34505             }
34506         }
34507     }
34508 };
34509
34510 /** 
34511  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34512  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34513  * Adapter that  moves the splitter element to align with the resized sizing element. 
34514  * Used with an absolute positioned SplitBar.
34515  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34516  * document.body, make sure you assign an id to the body element.
34517  */
34518 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34519     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34520     this.container = Roo.get(container);
34521 };
34522
34523 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34524     init : function(s){
34525         this.basic.init(s);
34526     },
34527     
34528     getElementSize : function(s){
34529         return this.basic.getElementSize(s);
34530     },
34531     
34532     setElementSize : function(s, newSize, onComplete){
34533         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34534     },
34535     
34536     moveSplitter : function(s){
34537         var yes = Roo.bootstrap.SplitBar;
34538         switch(s.placement){
34539             case yes.LEFT:
34540                 s.el.setX(s.resizingEl.getRight());
34541                 break;
34542             case yes.RIGHT:
34543                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34544                 break;
34545             case yes.TOP:
34546                 s.el.setY(s.resizingEl.getBottom());
34547                 break;
34548             case yes.BOTTOM:
34549                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34550                 break;
34551         }
34552     }
34553 };
34554
34555 /**
34556  * Orientation constant - Create a vertical SplitBar
34557  * @static
34558  * @type Number
34559  */
34560 Roo.bootstrap.SplitBar.VERTICAL = 1;
34561
34562 /**
34563  * Orientation constant - Create a horizontal SplitBar
34564  * @static
34565  * @type Number
34566  */
34567 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34568
34569 /**
34570  * Placement constant - The resizing element is to the left of the splitter element
34571  * @static
34572  * @type Number
34573  */
34574 Roo.bootstrap.SplitBar.LEFT = 1;
34575
34576 /**
34577  * Placement constant - The resizing element is to the right of the splitter element
34578  * @static
34579  * @type Number
34580  */
34581 Roo.bootstrap.SplitBar.RIGHT = 2;
34582
34583 /**
34584  * Placement constant - The resizing element is positioned above the splitter element
34585  * @static
34586  * @type Number
34587  */
34588 Roo.bootstrap.SplitBar.TOP = 3;
34589
34590 /**
34591  * Placement constant - The resizing element is positioned under splitter element
34592  * @static
34593  * @type Number
34594  */
34595 Roo.bootstrap.SplitBar.BOTTOM = 4;
34596 Roo.namespace("Roo.bootstrap.layout");/*
34597  * Based on:
34598  * Ext JS Library 1.1.1
34599  * Copyright(c) 2006-2007, Ext JS, LLC.
34600  *
34601  * Originally Released Under LGPL - original licence link has changed is not relivant.
34602  *
34603  * Fork - LGPL
34604  * <script type="text/javascript">
34605  */
34606
34607 /**
34608  * @class Roo.bootstrap.layout.Manager
34609  * @extends Roo.bootstrap.Component
34610  * Base class for layout managers.
34611  */
34612 Roo.bootstrap.layout.Manager = function(config)
34613 {
34614     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34615
34616
34617
34618
34619
34620     /** false to disable window resize monitoring @type Boolean */
34621     this.monitorWindowResize = true;
34622     this.regions = {};
34623     this.addEvents({
34624         /**
34625          * @event layout
34626          * Fires when a layout is performed.
34627          * @param {Roo.LayoutManager} this
34628          */
34629         "layout" : true,
34630         /**
34631          * @event regionresized
34632          * Fires when the user resizes a region.
34633          * @param {Roo.LayoutRegion} region The resized region
34634          * @param {Number} newSize The new size (width for east/west, height for north/south)
34635          */
34636         "regionresized" : true,
34637         /**
34638          * @event regioncollapsed
34639          * Fires when a region is collapsed.
34640          * @param {Roo.LayoutRegion} region The collapsed region
34641          */
34642         "regioncollapsed" : true,
34643         /**
34644          * @event regionexpanded
34645          * Fires when a region is expanded.
34646          * @param {Roo.LayoutRegion} region The expanded region
34647          */
34648         "regionexpanded" : true
34649     });
34650     this.updating = false;
34651
34652     if (config.el) {
34653         this.el = Roo.get(config.el);
34654         this.initEvents();
34655     }
34656
34657 };
34658
34659 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34660
34661
34662     regions : null,
34663
34664     monitorWindowResize : true,
34665
34666
34667     updating : false,
34668
34669
34670     onRender : function(ct, position)
34671     {
34672         if(!this.el){
34673             this.el = Roo.get(ct);
34674             this.initEvents();
34675         }
34676         //this.fireEvent('render',this);
34677     },
34678
34679
34680     initEvents: function()
34681     {
34682
34683
34684         // ie scrollbar fix
34685         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34686             document.body.scroll = "no";
34687         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34688             this.el.position('relative');
34689         }
34690         this.id = this.el.id;
34691         this.el.addClass("roo-layout-container");
34692         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34693         if(this.el.dom != document.body ) {
34694             this.el.on('resize', this.layout,this);
34695             this.el.on('show', this.layout,this);
34696         }
34697
34698     },
34699
34700     /**
34701      * Returns true if this layout is currently being updated
34702      * @return {Boolean}
34703      */
34704     isUpdating : function(){
34705         return this.updating;
34706     },
34707
34708     /**
34709      * Suspend the LayoutManager from doing auto-layouts while
34710      * making multiple add or remove calls
34711      */
34712     beginUpdate : function(){
34713         this.updating = true;
34714     },
34715
34716     /**
34717      * Restore auto-layouts and optionally disable the manager from performing a layout
34718      * @param {Boolean} noLayout true to disable a layout update
34719      */
34720     endUpdate : function(noLayout){
34721         this.updating = false;
34722         if(!noLayout){
34723             this.layout();
34724         }
34725     },
34726
34727     layout: function(){
34728         // abstract...
34729     },
34730
34731     onRegionResized : function(region, newSize){
34732         this.fireEvent("regionresized", region, newSize);
34733         this.layout();
34734     },
34735
34736     onRegionCollapsed : function(region){
34737         this.fireEvent("regioncollapsed", region);
34738     },
34739
34740     onRegionExpanded : function(region){
34741         this.fireEvent("regionexpanded", region);
34742     },
34743
34744     /**
34745      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34746      * performs box-model adjustments.
34747      * @return {Object} The size as an object {width: (the width), height: (the height)}
34748      */
34749     getViewSize : function()
34750     {
34751         var size;
34752         if(this.el.dom != document.body){
34753             size = this.el.getSize();
34754         }else{
34755             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34756         }
34757         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34758         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34759         return size;
34760     },
34761
34762     /**
34763      * Returns the Element this layout is bound to.
34764      * @return {Roo.Element}
34765      */
34766     getEl : function(){
34767         return this.el;
34768     },
34769
34770     /**
34771      * Returns the specified region.
34772      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34773      * @return {Roo.LayoutRegion}
34774      */
34775     getRegion : function(target){
34776         return this.regions[target.toLowerCase()];
34777     },
34778
34779     onWindowResize : function(){
34780         if(this.monitorWindowResize){
34781             this.layout();
34782         }
34783     }
34784 });
34785 /*
34786  * Based on:
34787  * Ext JS Library 1.1.1
34788  * Copyright(c) 2006-2007, Ext JS, LLC.
34789  *
34790  * Originally Released Under LGPL - original licence link has changed is not relivant.
34791  *
34792  * Fork - LGPL
34793  * <script type="text/javascript">
34794  */
34795 /**
34796  * @class Roo.bootstrap.layout.Border
34797  * @extends Roo.bootstrap.layout.Manager
34798  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34799  * please see: examples/bootstrap/nested.html<br><br>
34800  
34801 <b>The container the layout is rendered into can be either the body element or any other element.
34802 If it is not the body element, the container needs to either be an absolute positioned element,
34803 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34804 the container size if it is not the body element.</b>
34805
34806 * @constructor
34807 * Create a new Border
34808 * @param {Object} config Configuration options
34809  */
34810 Roo.bootstrap.layout.Border = function(config){
34811     config = config || {};
34812     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34813     
34814     
34815     
34816     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34817         if(config[region]){
34818             config[region].region = region;
34819             this.addRegion(config[region]);
34820         }
34821     },this);
34822     
34823 };
34824
34825 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34826
34827 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34828     /**
34829      * Creates and adds a new region if it doesn't already exist.
34830      * @param {String} target The target region key (north, south, east, west or center).
34831      * @param {Object} config The regions config object
34832      * @return {BorderLayoutRegion} The new region
34833      */
34834     addRegion : function(config)
34835     {
34836         if(!this.regions[config.region]){
34837             var r = this.factory(config);
34838             this.bindRegion(r);
34839         }
34840         return this.regions[config.region];
34841     },
34842
34843     // private (kinda)
34844     bindRegion : function(r){
34845         this.regions[r.config.region] = r;
34846         
34847         r.on("visibilitychange",    this.layout, this);
34848         r.on("paneladded",          this.layout, this);
34849         r.on("panelremoved",        this.layout, this);
34850         r.on("invalidated",         this.layout, this);
34851         r.on("resized",             this.onRegionResized, this);
34852         r.on("collapsed",           this.onRegionCollapsed, this);
34853         r.on("expanded",            this.onRegionExpanded, this);
34854     },
34855
34856     /**
34857      * Performs a layout update.
34858      */
34859     layout : function()
34860     {
34861         if(this.updating) {
34862             return;
34863         }
34864         
34865         // render all the rebions if they have not been done alreayd?
34866         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34867             if(this.regions[region] && !this.regions[region].bodyEl){
34868                 this.regions[region].onRender(this.el)
34869             }
34870         },this);
34871         
34872         var size = this.getViewSize();
34873         var w = size.width;
34874         var h = size.height;
34875         var centerW = w;
34876         var centerH = h;
34877         var centerY = 0;
34878         var centerX = 0;
34879         //var x = 0, y = 0;
34880
34881         var rs = this.regions;
34882         var north = rs["north"];
34883         var south = rs["south"]; 
34884         var west = rs["west"];
34885         var east = rs["east"];
34886         var center = rs["center"];
34887         //if(this.hideOnLayout){ // not supported anymore
34888             //c.el.setStyle("display", "none");
34889         //}
34890         if(north && north.isVisible()){
34891             var b = north.getBox();
34892             var m = north.getMargins();
34893             b.width = w - (m.left+m.right);
34894             b.x = m.left;
34895             b.y = m.top;
34896             centerY = b.height + b.y + m.bottom;
34897             centerH -= centerY;
34898             north.updateBox(this.safeBox(b));
34899         }
34900         if(south && south.isVisible()){
34901             var b = south.getBox();
34902             var m = south.getMargins();
34903             b.width = w - (m.left+m.right);
34904             b.x = m.left;
34905             var totalHeight = (b.height + m.top + m.bottom);
34906             b.y = h - totalHeight + m.top;
34907             centerH -= totalHeight;
34908             south.updateBox(this.safeBox(b));
34909         }
34910         if(west && west.isVisible()){
34911             var b = west.getBox();
34912             var m = west.getMargins();
34913             b.height = centerH - (m.top+m.bottom);
34914             b.x = m.left;
34915             b.y = centerY + m.top;
34916             var totalWidth = (b.width + m.left + m.right);
34917             centerX += totalWidth;
34918             centerW -= totalWidth;
34919             west.updateBox(this.safeBox(b));
34920         }
34921         if(east && east.isVisible()){
34922             var b = east.getBox();
34923             var m = east.getMargins();
34924             b.height = centerH - (m.top+m.bottom);
34925             var totalWidth = (b.width + m.left + m.right);
34926             b.x = w - totalWidth + m.left;
34927             b.y = centerY + m.top;
34928             centerW -= totalWidth;
34929             east.updateBox(this.safeBox(b));
34930         }
34931         if(center){
34932             var m = center.getMargins();
34933             var centerBox = {
34934                 x: centerX + m.left,
34935                 y: centerY + m.top,
34936                 width: centerW - (m.left+m.right),
34937                 height: centerH - (m.top+m.bottom)
34938             };
34939             //if(this.hideOnLayout){
34940                 //center.el.setStyle("display", "block");
34941             //}
34942             center.updateBox(this.safeBox(centerBox));
34943         }
34944         this.el.repaint();
34945         this.fireEvent("layout", this);
34946     },
34947
34948     // private
34949     safeBox : function(box){
34950         box.width = Math.max(0, box.width);
34951         box.height = Math.max(0, box.height);
34952         return box;
34953     },
34954
34955     /**
34956      * Adds a ContentPanel (or subclass) to this layout.
34957      * @param {String} target The target region key (north, south, east, west or center).
34958      * @param {Roo.ContentPanel} panel The panel to add
34959      * @return {Roo.ContentPanel} The added panel
34960      */
34961     add : function(target, panel){
34962          
34963         target = target.toLowerCase();
34964         return this.regions[target].add(panel);
34965     },
34966
34967     /**
34968      * Remove a ContentPanel (or subclass) to this layout.
34969      * @param {String} target The target region key (north, south, east, west or center).
34970      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34971      * @return {Roo.ContentPanel} The removed panel
34972      */
34973     remove : function(target, panel){
34974         target = target.toLowerCase();
34975         return this.regions[target].remove(panel);
34976     },
34977
34978     /**
34979      * Searches all regions for a panel with the specified id
34980      * @param {String} panelId
34981      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34982      */
34983     findPanel : function(panelId){
34984         var rs = this.regions;
34985         for(var target in rs){
34986             if(typeof rs[target] != "function"){
34987                 var p = rs[target].getPanel(panelId);
34988                 if(p){
34989                     return p;
34990                 }
34991             }
34992         }
34993         return null;
34994     },
34995
34996     /**
34997      * Searches all regions for a panel with the specified id and activates (shows) it.
34998      * @param {String/ContentPanel} panelId The panels id or the panel itself
34999      * @return {Roo.ContentPanel} The shown panel or null
35000      */
35001     showPanel : function(panelId) {
35002       var rs = this.regions;
35003       for(var target in rs){
35004          var r = rs[target];
35005          if(typeof r != "function"){
35006             if(r.hasPanel(panelId)){
35007                return r.showPanel(panelId);
35008             }
35009          }
35010       }
35011       return null;
35012    },
35013
35014    /**
35015      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35016      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35017      */
35018    /*
35019     restoreState : function(provider){
35020         if(!provider){
35021             provider = Roo.state.Manager;
35022         }
35023         var sm = new Roo.LayoutStateManager();
35024         sm.init(this, provider);
35025     },
35026 */
35027  
35028  
35029     /**
35030      * Adds a xtype elements to the layout.
35031      * <pre><code>
35032
35033 layout.addxtype({
35034        xtype : 'ContentPanel',
35035        region: 'west',
35036        items: [ .... ]
35037    }
35038 );
35039
35040 layout.addxtype({
35041         xtype : 'NestedLayoutPanel',
35042         region: 'west',
35043         layout: {
35044            center: { },
35045            west: { }   
35046         },
35047         items : [ ... list of content panels or nested layout panels.. ]
35048    }
35049 );
35050 </code></pre>
35051      * @param {Object} cfg Xtype definition of item to add.
35052      */
35053     addxtype : function(cfg)
35054     {
35055         // basically accepts a pannel...
35056         // can accept a layout region..!?!?
35057         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35058         
35059         
35060         // theory?  children can only be panels??
35061         
35062         //if (!cfg.xtype.match(/Panel$/)) {
35063         //    return false;
35064         //}
35065         var ret = false;
35066         
35067         if (typeof(cfg.region) == 'undefined') {
35068             Roo.log("Failed to add Panel, region was not set");
35069             Roo.log(cfg);
35070             return false;
35071         }
35072         var region = cfg.region;
35073         delete cfg.region;
35074         
35075           
35076         var xitems = [];
35077         if (cfg.items) {
35078             xitems = cfg.items;
35079             delete cfg.items;
35080         }
35081         var nb = false;
35082         
35083         switch(cfg.xtype) 
35084         {
35085             case 'Content':  // ContentPanel (el, cfg)
35086             case 'Scroll':  // ContentPanel (el, cfg)
35087             case 'View': 
35088                 cfg.autoCreate = true;
35089                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35090                 //} else {
35091                 //    var el = this.el.createChild();
35092                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35093                 //}
35094                 
35095                 this.add(region, ret);
35096                 break;
35097             
35098             /*
35099             case 'TreePanel': // our new panel!
35100                 cfg.el = this.el.createChild();
35101                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35102                 this.add(region, ret);
35103                 break;
35104             */
35105             
35106             case 'Nest': 
35107                 // create a new Layout (which is  a Border Layout...
35108                 
35109                 var clayout = cfg.layout;
35110                 clayout.el  = this.el.createChild();
35111                 clayout.items   = clayout.items  || [];
35112                 
35113                 delete cfg.layout;
35114                 
35115                 // replace this exitems with the clayout ones..
35116                 xitems = clayout.items;
35117                  
35118                 // force background off if it's in center...
35119                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35120                     cfg.background = false;
35121                 }
35122                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35123                 
35124                 
35125                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35126                 //console.log('adding nested layout panel '  + cfg.toSource());
35127                 this.add(region, ret);
35128                 nb = {}; /// find first...
35129                 break;
35130             
35131             case 'Grid':
35132                 
35133                 // needs grid and region
35134                 
35135                 //var el = this.getRegion(region).el.createChild();
35136                 /*
35137                  *var el = this.el.createChild();
35138                 // create the grid first...
35139                 cfg.grid.container = el;
35140                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35141                 */
35142                 
35143                 if (region == 'center' && this.active ) {
35144                     cfg.background = false;
35145                 }
35146                 
35147                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35148                 
35149                 this.add(region, ret);
35150                 /*
35151                 if (cfg.background) {
35152                     // render grid on panel activation (if panel background)
35153                     ret.on('activate', function(gp) {
35154                         if (!gp.grid.rendered) {
35155                     //        gp.grid.render(el);
35156                         }
35157                     });
35158                 } else {
35159                   //  cfg.grid.render(el);
35160                 }
35161                 */
35162                 break;
35163            
35164            
35165             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35166                 // it was the old xcomponent building that caused this before.
35167                 // espeically if border is the top element in the tree.
35168                 ret = this;
35169                 break; 
35170                 
35171                     
35172                 
35173                 
35174                 
35175             default:
35176                 /*
35177                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35178                     
35179                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35180                     this.add(region, ret);
35181                 } else {
35182                 */
35183                     Roo.log(cfg);
35184                     throw "Can not add '" + cfg.xtype + "' to Border";
35185                     return null;
35186              
35187                                 
35188              
35189         }
35190         this.beginUpdate();
35191         // add children..
35192         var region = '';
35193         var abn = {};
35194         Roo.each(xitems, function(i)  {
35195             region = nb && i.region ? i.region : false;
35196             
35197             var add = ret.addxtype(i);
35198            
35199             if (region) {
35200                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35201                 if (!i.background) {
35202                     abn[region] = nb[region] ;
35203                 }
35204             }
35205             
35206         });
35207         this.endUpdate();
35208
35209         // make the last non-background panel active..
35210         //if (nb) { Roo.log(abn); }
35211         if (nb) {
35212             
35213             for(var r in abn) {
35214                 region = this.getRegion(r);
35215                 if (region) {
35216                     // tried using nb[r], but it does not work..
35217                      
35218                     region.showPanel(abn[r]);
35219                    
35220                 }
35221             }
35222         }
35223         return ret;
35224         
35225     },
35226     
35227     
35228 // private
35229     factory : function(cfg)
35230     {
35231         
35232         var validRegions = Roo.bootstrap.layout.Border.regions;
35233
35234         var target = cfg.region;
35235         cfg.mgr = this;
35236         
35237         var r = Roo.bootstrap.layout;
35238         Roo.log(target);
35239         switch(target){
35240             case "north":
35241                 return new r.North(cfg);
35242             case "south":
35243                 return new r.South(cfg);
35244             case "east":
35245                 return new r.East(cfg);
35246             case "west":
35247                 return new r.West(cfg);
35248             case "center":
35249                 return new r.Center(cfg);
35250         }
35251         throw 'Layout region "'+target+'" not supported.';
35252     }
35253     
35254     
35255 });
35256  /*
35257  * Based on:
35258  * Ext JS Library 1.1.1
35259  * Copyright(c) 2006-2007, Ext JS, LLC.
35260  *
35261  * Originally Released Under LGPL - original licence link has changed is not relivant.
35262  *
35263  * Fork - LGPL
35264  * <script type="text/javascript">
35265  */
35266  
35267 /**
35268  * @class Roo.bootstrap.layout.Basic
35269  * @extends Roo.util.Observable
35270  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35271  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35272  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35273  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35274  * @cfg {string}   region  the region that it inhabits..
35275  * @cfg {bool}   skipConfig skip config?
35276  * 
35277
35278  */
35279 Roo.bootstrap.layout.Basic = function(config){
35280     
35281     this.mgr = config.mgr;
35282     
35283     this.position = config.region;
35284     
35285     var skipConfig = config.skipConfig;
35286     
35287     this.events = {
35288         /**
35289          * @scope Roo.BasicLayoutRegion
35290          */
35291         
35292         /**
35293          * @event beforeremove
35294          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35295          * @param {Roo.LayoutRegion} this
35296          * @param {Roo.ContentPanel} panel The panel
35297          * @param {Object} e The cancel event object
35298          */
35299         "beforeremove" : true,
35300         /**
35301          * @event invalidated
35302          * Fires when the layout for this region is changed.
35303          * @param {Roo.LayoutRegion} this
35304          */
35305         "invalidated" : true,
35306         /**
35307          * @event visibilitychange
35308          * Fires when this region is shown or hidden 
35309          * @param {Roo.LayoutRegion} this
35310          * @param {Boolean} visibility true or false
35311          */
35312         "visibilitychange" : true,
35313         /**
35314          * @event paneladded
35315          * Fires when a panel is added. 
35316          * @param {Roo.LayoutRegion} this
35317          * @param {Roo.ContentPanel} panel The panel
35318          */
35319         "paneladded" : true,
35320         /**
35321          * @event panelremoved
35322          * Fires when a panel is removed. 
35323          * @param {Roo.LayoutRegion} this
35324          * @param {Roo.ContentPanel} panel The panel
35325          */
35326         "panelremoved" : true,
35327         /**
35328          * @event beforecollapse
35329          * Fires when this region before collapse.
35330          * @param {Roo.LayoutRegion} this
35331          */
35332         "beforecollapse" : true,
35333         /**
35334          * @event collapsed
35335          * Fires when this region is collapsed.
35336          * @param {Roo.LayoutRegion} this
35337          */
35338         "collapsed" : true,
35339         /**
35340          * @event expanded
35341          * Fires when this region is expanded.
35342          * @param {Roo.LayoutRegion} this
35343          */
35344         "expanded" : true,
35345         /**
35346          * @event slideshow
35347          * Fires when this region is slid into view.
35348          * @param {Roo.LayoutRegion} this
35349          */
35350         "slideshow" : true,
35351         /**
35352          * @event slidehide
35353          * Fires when this region slides out of view. 
35354          * @param {Roo.LayoutRegion} this
35355          */
35356         "slidehide" : true,
35357         /**
35358          * @event panelactivated
35359          * Fires when a panel is activated. 
35360          * @param {Roo.LayoutRegion} this
35361          * @param {Roo.ContentPanel} panel The activated panel
35362          */
35363         "panelactivated" : true,
35364         /**
35365          * @event resized
35366          * Fires when the user resizes this region. 
35367          * @param {Roo.LayoutRegion} this
35368          * @param {Number} newSize The new size (width for east/west, height for north/south)
35369          */
35370         "resized" : true
35371     };
35372     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35373     this.panels = new Roo.util.MixedCollection();
35374     this.panels.getKey = this.getPanelId.createDelegate(this);
35375     this.box = null;
35376     this.activePanel = null;
35377     // ensure listeners are added...
35378     
35379     if (config.listeners || config.events) {
35380         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35381             listeners : config.listeners || {},
35382             events : config.events || {}
35383         });
35384     }
35385     
35386     if(skipConfig !== true){
35387         this.applyConfig(config);
35388     }
35389 };
35390
35391 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35392 {
35393     getPanelId : function(p){
35394         return p.getId();
35395     },
35396     
35397     applyConfig : function(config){
35398         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35399         this.config = config;
35400         
35401     },
35402     
35403     /**
35404      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35405      * the width, for horizontal (north, south) the height.
35406      * @param {Number} newSize The new width or height
35407      */
35408     resizeTo : function(newSize){
35409         var el = this.el ? this.el :
35410                  (this.activePanel ? this.activePanel.getEl() : null);
35411         if(el){
35412             switch(this.position){
35413                 case "east":
35414                 case "west":
35415                     el.setWidth(newSize);
35416                     this.fireEvent("resized", this, newSize);
35417                 break;
35418                 case "north":
35419                 case "south":
35420                     el.setHeight(newSize);
35421                     this.fireEvent("resized", this, newSize);
35422                 break;                
35423             }
35424         }
35425     },
35426     
35427     getBox : function(){
35428         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35429     },
35430     
35431     getMargins : function(){
35432         return this.margins;
35433     },
35434     
35435     updateBox : function(box){
35436         this.box = box;
35437         var el = this.activePanel.getEl();
35438         el.dom.style.left = box.x + "px";
35439         el.dom.style.top = box.y + "px";
35440         this.activePanel.setSize(box.width, box.height);
35441     },
35442     
35443     /**
35444      * Returns the container element for this region.
35445      * @return {Roo.Element}
35446      */
35447     getEl : function(){
35448         return this.activePanel;
35449     },
35450     
35451     /**
35452      * Returns true if this region is currently visible.
35453      * @return {Boolean}
35454      */
35455     isVisible : function(){
35456         return this.activePanel ? true : false;
35457     },
35458     
35459     setActivePanel : function(panel){
35460         panel = this.getPanel(panel);
35461         if(this.activePanel && this.activePanel != panel){
35462             this.activePanel.setActiveState(false);
35463             this.activePanel.getEl().setLeftTop(-10000,-10000);
35464         }
35465         this.activePanel = panel;
35466         panel.setActiveState(true);
35467         if(this.box){
35468             panel.setSize(this.box.width, this.box.height);
35469         }
35470         this.fireEvent("panelactivated", this, panel);
35471         this.fireEvent("invalidated");
35472     },
35473     
35474     /**
35475      * Show the specified panel.
35476      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35477      * @return {Roo.ContentPanel} The shown panel or null
35478      */
35479     showPanel : function(panel){
35480         panel = this.getPanel(panel);
35481         if(panel){
35482             this.setActivePanel(panel);
35483         }
35484         return panel;
35485     },
35486     
35487     /**
35488      * Get the active panel for this region.
35489      * @return {Roo.ContentPanel} The active panel or null
35490      */
35491     getActivePanel : function(){
35492         return this.activePanel;
35493     },
35494     
35495     /**
35496      * Add the passed ContentPanel(s)
35497      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35498      * @return {Roo.ContentPanel} The panel added (if only one was added)
35499      */
35500     add : function(panel){
35501         if(arguments.length > 1){
35502             for(var i = 0, len = arguments.length; i < len; i++) {
35503                 this.add(arguments[i]);
35504             }
35505             return null;
35506         }
35507         if(this.hasPanel(panel)){
35508             this.showPanel(panel);
35509             return panel;
35510         }
35511         var el = panel.getEl();
35512         if(el.dom.parentNode != this.mgr.el.dom){
35513             this.mgr.el.dom.appendChild(el.dom);
35514         }
35515         if(panel.setRegion){
35516             panel.setRegion(this);
35517         }
35518         this.panels.add(panel);
35519         el.setStyle("position", "absolute");
35520         if(!panel.background){
35521             this.setActivePanel(panel);
35522             if(this.config.initialSize && this.panels.getCount()==1){
35523                 this.resizeTo(this.config.initialSize);
35524             }
35525         }
35526         this.fireEvent("paneladded", this, panel);
35527         return panel;
35528     },
35529     
35530     /**
35531      * Returns true if the panel is in this region.
35532      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35533      * @return {Boolean}
35534      */
35535     hasPanel : function(panel){
35536         if(typeof panel == "object"){ // must be panel obj
35537             panel = panel.getId();
35538         }
35539         return this.getPanel(panel) ? true : false;
35540     },
35541     
35542     /**
35543      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35544      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35545      * @param {Boolean} preservePanel Overrides the config preservePanel option
35546      * @return {Roo.ContentPanel} The panel that was removed
35547      */
35548     remove : function(panel, preservePanel){
35549         panel = this.getPanel(panel);
35550         if(!panel){
35551             return null;
35552         }
35553         var e = {};
35554         this.fireEvent("beforeremove", this, panel, e);
35555         if(e.cancel === true){
35556             return null;
35557         }
35558         var panelId = panel.getId();
35559         this.panels.removeKey(panelId);
35560         return panel;
35561     },
35562     
35563     /**
35564      * Returns the panel specified or null if it's not in this region.
35565      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35566      * @return {Roo.ContentPanel}
35567      */
35568     getPanel : function(id){
35569         if(typeof id == "object"){ // must be panel obj
35570             return id;
35571         }
35572         return this.panels.get(id);
35573     },
35574     
35575     /**
35576      * Returns this regions position (north/south/east/west/center).
35577      * @return {String} 
35578      */
35579     getPosition: function(){
35580         return this.position;    
35581     }
35582 });/*
35583  * Based on:
35584  * Ext JS Library 1.1.1
35585  * Copyright(c) 2006-2007, Ext JS, LLC.
35586  *
35587  * Originally Released Under LGPL - original licence link has changed is not relivant.
35588  *
35589  * Fork - LGPL
35590  * <script type="text/javascript">
35591  */
35592  
35593 /**
35594  * @class Roo.bootstrap.layout.Region
35595  * @extends Roo.bootstrap.layout.Basic
35596  * This class represents a region in a layout manager.
35597  
35598  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35599  * @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})
35600  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35601  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35602  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35603  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35604  * @cfg {String}    title           The title for the region (overrides panel titles)
35605  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35606  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35607  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35608  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35609  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35610  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35611  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35612  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35613  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35614  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35615
35616  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35617  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35618  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35619  * @cfg {Number}    width           For East/West panels
35620  * @cfg {Number}    height          For North/South panels
35621  * @cfg {Boolean}   split           To show the splitter
35622  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35623  * 
35624  * @cfg {string}   cls             Extra CSS classes to add to region
35625  * 
35626  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35627  * @cfg {string}   region  the region that it inhabits..
35628  *
35629
35630  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35631  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35632
35633  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35634  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35635  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35636  */
35637 Roo.bootstrap.layout.Region = function(config)
35638 {
35639     this.applyConfig(config);
35640
35641     var mgr = config.mgr;
35642     var pos = config.region;
35643     config.skipConfig = true;
35644     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35645     
35646     if (mgr.el) {
35647         this.onRender(mgr.el);   
35648     }
35649      
35650     this.visible = true;
35651     this.collapsed = false;
35652     this.unrendered_panels = [];
35653 };
35654
35655 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35656
35657     position: '', // set by wrapper (eg. north/south etc..)
35658     unrendered_panels : null,  // unrendered panels.
35659     createBody : function(){
35660         /** This region's body element 
35661         * @type Roo.Element */
35662         this.bodyEl = this.el.createChild({
35663                 tag: "div",
35664                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35665         });
35666     },
35667
35668     onRender: function(ctr, pos)
35669     {
35670         var dh = Roo.DomHelper;
35671         /** This region's container element 
35672         * @type Roo.Element */
35673         this.el = dh.append(ctr.dom, {
35674                 tag: "div",
35675                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35676             }, true);
35677         /** This region's title element 
35678         * @type Roo.Element */
35679     
35680         this.titleEl = dh.append(this.el.dom,
35681             {
35682                     tag: "div",
35683                     unselectable: "on",
35684                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35685                     children:[
35686                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35687                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35688                     ]}, true);
35689         
35690         this.titleEl.enableDisplayMode();
35691         /** This region's title text element 
35692         * @type HTMLElement */
35693         this.titleTextEl = this.titleEl.dom.firstChild;
35694         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35695         /*
35696         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35697         this.closeBtn.enableDisplayMode();
35698         this.closeBtn.on("click", this.closeClicked, this);
35699         this.closeBtn.hide();
35700     */
35701         this.createBody(this.config);
35702         if(this.config.hideWhenEmpty){
35703             this.hide();
35704             this.on("paneladded", this.validateVisibility, this);
35705             this.on("panelremoved", this.validateVisibility, this);
35706         }
35707         if(this.autoScroll){
35708             this.bodyEl.setStyle("overflow", "auto");
35709         }else{
35710             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35711         }
35712         //if(c.titlebar !== false){
35713             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35714                 this.titleEl.hide();
35715             }else{
35716                 this.titleEl.show();
35717                 if(this.config.title){
35718                     this.titleTextEl.innerHTML = this.config.title;
35719                 }
35720             }
35721         //}
35722         if(this.config.collapsed){
35723             this.collapse(true);
35724         }
35725         if(this.config.hidden){
35726             this.hide();
35727         }
35728         
35729         if (this.unrendered_panels && this.unrendered_panels.length) {
35730             for (var i =0;i< this.unrendered_panels.length; i++) {
35731                 this.add(this.unrendered_panels[i]);
35732             }
35733             this.unrendered_panels = null;
35734             
35735         }
35736         
35737     },
35738     
35739     applyConfig : function(c)
35740     {
35741         /*
35742          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35743             var dh = Roo.DomHelper;
35744             if(c.titlebar !== false){
35745                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35746                 this.collapseBtn.on("click", this.collapse, this);
35747                 this.collapseBtn.enableDisplayMode();
35748                 /*
35749                 if(c.showPin === true || this.showPin){
35750                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35751                     this.stickBtn.enableDisplayMode();
35752                     this.stickBtn.on("click", this.expand, this);
35753                     this.stickBtn.hide();
35754                 }
35755                 
35756             }
35757             */
35758             /** This region's collapsed element
35759             * @type Roo.Element */
35760             /*
35761              *
35762             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35763                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35764             ]}, true);
35765             
35766             if(c.floatable !== false){
35767                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35768                this.collapsedEl.on("click", this.collapseClick, this);
35769             }
35770
35771             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35772                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35773                    id: "message", unselectable: "on", style:{"float":"left"}});
35774                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35775              }
35776             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35777             this.expandBtn.on("click", this.expand, this);
35778             
35779         }
35780         
35781         if(this.collapseBtn){
35782             this.collapseBtn.setVisible(c.collapsible == true);
35783         }
35784         
35785         this.cmargins = c.cmargins || this.cmargins ||
35786                          (this.position == "west" || this.position == "east" ?
35787                              {top: 0, left: 2, right:2, bottom: 0} :
35788                              {top: 2, left: 0, right:0, bottom: 2});
35789         */
35790         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35791         
35792         
35793         this.bottomTabs = c.tabPosition != "top";
35794         
35795         this.autoScroll = c.autoScroll || false;
35796         
35797         
35798        
35799         
35800         this.duration = c.duration || .30;
35801         this.slideDuration = c.slideDuration || .45;
35802         this.config = c;
35803        
35804     },
35805     /**
35806      * Returns true if this region is currently visible.
35807      * @return {Boolean}
35808      */
35809     isVisible : function(){
35810         return this.visible;
35811     },
35812
35813     /**
35814      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35815      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35816      */
35817     //setCollapsedTitle : function(title){
35818     //    title = title || "&#160;";
35819      //   if(this.collapsedTitleTextEl){
35820       //      this.collapsedTitleTextEl.innerHTML = title;
35821        // }
35822     //},
35823
35824     getBox : function(){
35825         var b;
35826       //  if(!this.collapsed){
35827             b = this.el.getBox(false, true);
35828        // }else{
35829           //  b = this.collapsedEl.getBox(false, true);
35830         //}
35831         return b;
35832     },
35833
35834     getMargins : function(){
35835         return this.margins;
35836         //return this.collapsed ? this.cmargins : this.margins;
35837     },
35838 /*
35839     highlight : function(){
35840         this.el.addClass("x-layout-panel-dragover");
35841     },
35842
35843     unhighlight : function(){
35844         this.el.removeClass("x-layout-panel-dragover");
35845     },
35846 */
35847     updateBox : function(box)
35848     {
35849         if (!this.bodyEl) {
35850             return; // not rendered yet..
35851         }
35852         
35853         this.box = box;
35854         if(!this.collapsed){
35855             this.el.dom.style.left = box.x + "px";
35856             this.el.dom.style.top = box.y + "px";
35857             this.updateBody(box.width, box.height);
35858         }else{
35859             this.collapsedEl.dom.style.left = box.x + "px";
35860             this.collapsedEl.dom.style.top = box.y + "px";
35861             this.collapsedEl.setSize(box.width, box.height);
35862         }
35863         if(this.tabs){
35864             this.tabs.autoSizeTabs();
35865         }
35866     },
35867
35868     updateBody : function(w, h)
35869     {
35870         if(w !== null){
35871             this.el.setWidth(w);
35872             w -= this.el.getBorderWidth("rl");
35873             if(this.config.adjustments){
35874                 w += this.config.adjustments[0];
35875             }
35876         }
35877         if(h !== null && h > 0){
35878             this.el.setHeight(h);
35879             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35880             h -= this.el.getBorderWidth("tb");
35881             if(this.config.adjustments){
35882                 h += this.config.adjustments[1];
35883             }
35884             this.bodyEl.setHeight(h);
35885             if(this.tabs){
35886                 h = this.tabs.syncHeight(h);
35887             }
35888         }
35889         if(this.panelSize){
35890             w = w !== null ? w : this.panelSize.width;
35891             h = h !== null ? h : this.panelSize.height;
35892         }
35893         if(this.activePanel){
35894             var el = this.activePanel.getEl();
35895             w = w !== null ? w : el.getWidth();
35896             h = h !== null ? h : el.getHeight();
35897             this.panelSize = {width: w, height: h};
35898             this.activePanel.setSize(w, h);
35899         }
35900         if(Roo.isIE && this.tabs){
35901             this.tabs.el.repaint();
35902         }
35903     },
35904
35905     /**
35906      * Returns the container element for this region.
35907      * @return {Roo.Element}
35908      */
35909     getEl : function(){
35910         return this.el;
35911     },
35912
35913     /**
35914      * Hides this region.
35915      */
35916     hide : function(){
35917         //if(!this.collapsed){
35918             this.el.dom.style.left = "-2000px";
35919             this.el.hide();
35920         //}else{
35921          //   this.collapsedEl.dom.style.left = "-2000px";
35922          //   this.collapsedEl.hide();
35923        // }
35924         this.visible = false;
35925         this.fireEvent("visibilitychange", this, false);
35926     },
35927
35928     /**
35929      * Shows this region if it was previously hidden.
35930      */
35931     show : function(){
35932         //if(!this.collapsed){
35933             this.el.show();
35934         //}else{
35935         //    this.collapsedEl.show();
35936        // }
35937         this.visible = true;
35938         this.fireEvent("visibilitychange", this, true);
35939     },
35940 /*
35941     closeClicked : function(){
35942         if(this.activePanel){
35943             this.remove(this.activePanel);
35944         }
35945     },
35946
35947     collapseClick : function(e){
35948         if(this.isSlid){
35949            e.stopPropagation();
35950            this.slideIn();
35951         }else{
35952            e.stopPropagation();
35953            this.slideOut();
35954         }
35955     },
35956 */
35957     /**
35958      * Collapses this region.
35959      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35960      */
35961     /*
35962     collapse : function(skipAnim, skipCheck = false){
35963         if(this.collapsed) {
35964             return;
35965         }
35966         
35967         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35968             
35969             this.collapsed = true;
35970             if(this.split){
35971                 this.split.el.hide();
35972             }
35973             if(this.config.animate && skipAnim !== true){
35974                 this.fireEvent("invalidated", this);
35975                 this.animateCollapse();
35976             }else{
35977                 this.el.setLocation(-20000,-20000);
35978                 this.el.hide();
35979                 this.collapsedEl.show();
35980                 this.fireEvent("collapsed", this);
35981                 this.fireEvent("invalidated", this);
35982             }
35983         }
35984         
35985     },
35986 */
35987     animateCollapse : function(){
35988         // overridden
35989     },
35990
35991     /**
35992      * Expands this region if it was previously collapsed.
35993      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35994      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35995      */
35996     /*
35997     expand : function(e, skipAnim){
35998         if(e) {
35999             e.stopPropagation();
36000         }
36001         if(!this.collapsed || this.el.hasActiveFx()) {
36002             return;
36003         }
36004         if(this.isSlid){
36005             this.afterSlideIn();
36006             skipAnim = true;
36007         }
36008         this.collapsed = false;
36009         if(this.config.animate && skipAnim !== true){
36010             this.animateExpand();
36011         }else{
36012             this.el.show();
36013             if(this.split){
36014                 this.split.el.show();
36015             }
36016             this.collapsedEl.setLocation(-2000,-2000);
36017             this.collapsedEl.hide();
36018             this.fireEvent("invalidated", this);
36019             this.fireEvent("expanded", this);
36020         }
36021     },
36022 */
36023     animateExpand : function(){
36024         // overridden
36025     },
36026
36027     initTabs : function()
36028     {
36029         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36030         
36031         var ts = new Roo.bootstrap.panel.Tabs({
36032                 el: this.bodyEl.dom,
36033                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36034                 disableTooltips: this.config.disableTabTips,
36035                 toolbar : this.config.toolbar
36036             });
36037         
36038         if(this.config.hideTabs){
36039             ts.stripWrap.setDisplayed(false);
36040         }
36041         this.tabs = ts;
36042         ts.resizeTabs = this.config.resizeTabs === true;
36043         ts.minTabWidth = this.config.minTabWidth || 40;
36044         ts.maxTabWidth = this.config.maxTabWidth || 250;
36045         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36046         ts.monitorResize = false;
36047         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36048         ts.bodyEl.addClass('roo-layout-tabs-body');
36049         this.panels.each(this.initPanelAsTab, this);
36050     },
36051
36052     initPanelAsTab : function(panel){
36053         var ti = this.tabs.addTab(
36054             panel.getEl().id,
36055             panel.getTitle(),
36056             null,
36057             this.config.closeOnTab && panel.isClosable(),
36058             panel.tpl
36059         );
36060         if(panel.tabTip !== undefined){
36061             ti.setTooltip(panel.tabTip);
36062         }
36063         ti.on("activate", function(){
36064               this.setActivePanel(panel);
36065         }, this);
36066         
36067         if(this.config.closeOnTab){
36068             ti.on("beforeclose", function(t, e){
36069                 e.cancel = true;
36070                 this.remove(panel);
36071             }, this);
36072         }
36073         
36074         panel.tabItem = ti;
36075         
36076         return ti;
36077     },
36078
36079     updatePanelTitle : function(panel, title)
36080     {
36081         if(this.activePanel == panel){
36082             this.updateTitle(title);
36083         }
36084         if(this.tabs){
36085             var ti = this.tabs.getTab(panel.getEl().id);
36086             ti.setText(title);
36087             if(panel.tabTip !== undefined){
36088                 ti.setTooltip(panel.tabTip);
36089             }
36090         }
36091     },
36092
36093     updateTitle : function(title){
36094         if(this.titleTextEl && !this.config.title){
36095             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36096         }
36097     },
36098
36099     setActivePanel : function(panel)
36100     {
36101         panel = this.getPanel(panel);
36102         if(this.activePanel && this.activePanel != panel){
36103             if(this.activePanel.setActiveState(false) === false){
36104                 return;
36105             }
36106         }
36107         this.activePanel = panel;
36108         panel.setActiveState(true);
36109         if(this.panelSize){
36110             panel.setSize(this.panelSize.width, this.panelSize.height);
36111         }
36112         if(this.closeBtn){
36113             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36114         }
36115         this.updateTitle(panel.getTitle());
36116         if(this.tabs){
36117             this.fireEvent("invalidated", this);
36118         }
36119         this.fireEvent("panelactivated", this, panel);
36120     },
36121
36122     /**
36123      * Shows the specified panel.
36124      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36125      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36126      */
36127     showPanel : function(panel)
36128     {
36129         panel = this.getPanel(panel);
36130         if(panel){
36131             if(this.tabs){
36132                 var tab = this.tabs.getTab(panel.getEl().id);
36133                 if(tab.isHidden()){
36134                     this.tabs.unhideTab(tab.id);
36135                 }
36136                 tab.activate();
36137             }else{
36138                 this.setActivePanel(panel);
36139             }
36140         }
36141         return panel;
36142     },
36143
36144     /**
36145      * Get the active panel for this region.
36146      * @return {Roo.ContentPanel} The active panel or null
36147      */
36148     getActivePanel : function(){
36149         return this.activePanel;
36150     },
36151
36152     validateVisibility : function(){
36153         if(this.panels.getCount() < 1){
36154             this.updateTitle("&#160;");
36155             this.closeBtn.hide();
36156             this.hide();
36157         }else{
36158             if(!this.isVisible()){
36159                 this.show();
36160             }
36161         }
36162     },
36163
36164     /**
36165      * Adds the passed ContentPanel(s) to this region.
36166      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36167      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36168      */
36169     add : function(panel)
36170     {
36171         if(arguments.length > 1){
36172             for(var i = 0, len = arguments.length; i < len; i++) {
36173                 this.add(arguments[i]);
36174             }
36175             return null;
36176         }
36177         
36178         // if we have not been rendered yet, then we can not really do much of this..
36179         if (!this.bodyEl) {
36180             this.unrendered_panels.push(panel);
36181             return panel;
36182         }
36183         
36184         
36185         
36186         
36187         if(this.hasPanel(panel)){
36188             this.showPanel(panel);
36189             return panel;
36190         }
36191         panel.setRegion(this);
36192         this.panels.add(panel);
36193        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36194             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36195             // and hide them... ???
36196             this.bodyEl.dom.appendChild(panel.getEl().dom);
36197             if(panel.background !== true){
36198                 this.setActivePanel(panel);
36199             }
36200             this.fireEvent("paneladded", this, panel);
36201             return panel;
36202         }
36203         */
36204         if(!this.tabs){
36205             this.initTabs();
36206         }else{
36207             this.initPanelAsTab(panel);
36208         }
36209         
36210         
36211         if(panel.background !== true){
36212             this.tabs.activate(panel.getEl().id);
36213         }
36214         this.fireEvent("paneladded", this, panel);
36215         return panel;
36216     },
36217
36218     /**
36219      * Hides the tab for the specified panel.
36220      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36221      */
36222     hidePanel : function(panel){
36223         if(this.tabs && (panel = this.getPanel(panel))){
36224             this.tabs.hideTab(panel.getEl().id);
36225         }
36226     },
36227
36228     /**
36229      * Unhides the tab for a previously hidden panel.
36230      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36231      */
36232     unhidePanel : function(panel){
36233         if(this.tabs && (panel = this.getPanel(panel))){
36234             this.tabs.unhideTab(panel.getEl().id);
36235         }
36236     },
36237
36238     clearPanels : function(){
36239         while(this.panels.getCount() > 0){
36240              this.remove(this.panels.first());
36241         }
36242     },
36243
36244     /**
36245      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36246      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36247      * @param {Boolean} preservePanel Overrides the config preservePanel option
36248      * @return {Roo.ContentPanel} The panel that was removed
36249      */
36250     remove : function(panel, preservePanel)
36251     {
36252         panel = this.getPanel(panel);
36253         if(!panel){
36254             return null;
36255         }
36256         var e = {};
36257         this.fireEvent("beforeremove", this, panel, e);
36258         if(e.cancel === true){
36259             return null;
36260         }
36261         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36262         var panelId = panel.getId();
36263         this.panels.removeKey(panelId);
36264         if(preservePanel){
36265             document.body.appendChild(panel.getEl().dom);
36266         }
36267         if(this.tabs){
36268             this.tabs.removeTab(panel.getEl().id);
36269         }else if (!preservePanel){
36270             this.bodyEl.dom.removeChild(panel.getEl().dom);
36271         }
36272         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36273             var p = this.panels.first();
36274             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36275             tempEl.appendChild(p.getEl().dom);
36276             this.bodyEl.update("");
36277             this.bodyEl.dom.appendChild(p.getEl().dom);
36278             tempEl = null;
36279             this.updateTitle(p.getTitle());
36280             this.tabs = null;
36281             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36282             this.setActivePanel(p);
36283         }
36284         panel.setRegion(null);
36285         if(this.activePanel == panel){
36286             this.activePanel = null;
36287         }
36288         if(this.config.autoDestroy !== false && preservePanel !== true){
36289             try{panel.destroy();}catch(e){}
36290         }
36291         this.fireEvent("panelremoved", this, panel);
36292         return panel;
36293     },
36294
36295     /**
36296      * Returns the TabPanel component used by this region
36297      * @return {Roo.TabPanel}
36298      */
36299     getTabs : function(){
36300         return this.tabs;
36301     },
36302
36303     createTool : function(parentEl, className){
36304         var btn = Roo.DomHelper.append(parentEl, {
36305             tag: "div",
36306             cls: "x-layout-tools-button",
36307             children: [ {
36308                 tag: "div",
36309                 cls: "roo-layout-tools-button-inner " + className,
36310                 html: "&#160;"
36311             }]
36312         }, true);
36313         btn.addClassOnOver("roo-layout-tools-button-over");
36314         return btn;
36315     }
36316 });/*
36317  * Based on:
36318  * Ext JS Library 1.1.1
36319  * Copyright(c) 2006-2007, Ext JS, LLC.
36320  *
36321  * Originally Released Under LGPL - original licence link has changed is not relivant.
36322  *
36323  * Fork - LGPL
36324  * <script type="text/javascript">
36325  */
36326  
36327
36328
36329 /**
36330  * @class Roo.SplitLayoutRegion
36331  * @extends Roo.LayoutRegion
36332  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36333  */
36334 Roo.bootstrap.layout.Split = function(config){
36335     this.cursor = config.cursor;
36336     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36337 };
36338
36339 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36340 {
36341     splitTip : "Drag to resize.",
36342     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36343     useSplitTips : false,
36344
36345     applyConfig : function(config){
36346         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36347     },
36348     
36349     onRender : function(ctr,pos) {
36350         
36351         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36352         if(!this.config.split){
36353             return;
36354         }
36355         if(!this.split){
36356             
36357             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36358                             tag: "div",
36359                             id: this.el.id + "-split",
36360                             cls: "roo-layout-split roo-layout-split-"+this.position,
36361                             html: "&#160;"
36362             });
36363             /** The SplitBar for this region 
36364             * @type Roo.SplitBar */
36365             // does not exist yet...
36366             Roo.log([this.position, this.orientation]);
36367             
36368             this.split = new Roo.bootstrap.SplitBar({
36369                 dragElement : splitEl,
36370                 resizingElement: this.el,
36371                 orientation : this.orientation
36372             });
36373             
36374             this.split.on("moved", this.onSplitMove, this);
36375             this.split.useShim = this.config.useShim === true;
36376             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36377             if(this.useSplitTips){
36378                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36379             }
36380             //if(config.collapsible){
36381             //    this.split.el.on("dblclick", this.collapse,  this);
36382             //}
36383         }
36384         if(typeof this.config.minSize != "undefined"){
36385             this.split.minSize = this.config.minSize;
36386         }
36387         if(typeof this.config.maxSize != "undefined"){
36388             this.split.maxSize = this.config.maxSize;
36389         }
36390         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36391             this.hideSplitter();
36392         }
36393         
36394     },
36395
36396     getHMaxSize : function(){
36397          var cmax = this.config.maxSize || 10000;
36398          var center = this.mgr.getRegion("center");
36399          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36400     },
36401
36402     getVMaxSize : function(){
36403          var cmax = this.config.maxSize || 10000;
36404          var center = this.mgr.getRegion("center");
36405          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36406     },
36407
36408     onSplitMove : function(split, newSize){
36409         this.fireEvent("resized", this, newSize);
36410     },
36411     
36412     /** 
36413      * Returns the {@link Roo.SplitBar} for this region.
36414      * @return {Roo.SplitBar}
36415      */
36416     getSplitBar : function(){
36417         return this.split;
36418     },
36419     
36420     hide : function(){
36421         this.hideSplitter();
36422         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36423     },
36424
36425     hideSplitter : function(){
36426         if(this.split){
36427             this.split.el.setLocation(-2000,-2000);
36428             this.split.el.hide();
36429         }
36430     },
36431
36432     show : function(){
36433         if(this.split){
36434             this.split.el.show();
36435         }
36436         Roo.bootstrap.layout.Split.superclass.show.call(this);
36437     },
36438     
36439     beforeSlide: function(){
36440         if(Roo.isGecko){// firefox overflow auto bug workaround
36441             this.bodyEl.clip();
36442             if(this.tabs) {
36443                 this.tabs.bodyEl.clip();
36444             }
36445             if(this.activePanel){
36446                 this.activePanel.getEl().clip();
36447                 
36448                 if(this.activePanel.beforeSlide){
36449                     this.activePanel.beforeSlide();
36450                 }
36451             }
36452         }
36453     },
36454     
36455     afterSlide : function(){
36456         if(Roo.isGecko){// firefox overflow auto bug workaround
36457             this.bodyEl.unclip();
36458             if(this.tabs) {
36459                 this.tabs.bodyEl.unclip();
36460             }
36461             if(this.activePanel){
36462                 this.activePanel.getEl().unclip();
36463                 if(this.activePanel.afterSlide){
36464                     this.activePanel.afterSlide();
36465                 }
36466             }
36467         }
36468     },
36469
36470     initAutoHide : function(){
36471         if(this.autoHide !== false){
36472             if(!this.autoHideHd){
36473                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36474                 this.autoHideHd = {
36475                     "mouseout": function(e){
36476                         if(!e.within(this.el, true)){
36477                             st.delay(500);
36478                         }
36479                     },
36480                     "mouseover" : function(e){
36481                         st.cancel();
36482                     },
36483                     scope : this
36484                 };
36485             }
36486             this.el.on(this.autoHideHd);
36487         }
36488     },
36489
36490     clearAutoHide : function(){
36491         if(this.autoHide !== false){
36492             this.el.un("mouseout", this.autoHideHd.mouseout);
36493             this.el.un("mouseover", this.autoHideHd.mouseover);
36494         }
36495     },
36496
36497     clearMonitor : function(){
36498         Roo.get(document).un("click", this.slideInIf, this);
36499     },
36500
36501     // these names are backwards but not changed for compat
36502     slideOut : function(){
36503         if(this.isSlid || this.el.hasActiveFx()){
36504             return;
36505         }
36506         this.isSlid = true;
36507         if(this.collapseBtn){
36508             this.collapseBtn.hide();
36509         }
36510         this.closeBtnState = this.closeBtn.getStyle('display');
36511         this.closeBtn.hide();
36512         if(this.stickBtn){
36513             this.stickBtn.show();
36514         }
36515         this.el.show();
36516         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36517         this.beforeSlide();
36518         this.el.setStyle("z-index", 10001);
36519         this.el.slideIn(this.getSlideAnchor(), {
36520             callback: function(){
36521                 this.afterSlide();
36522                 this.initAutoHide();
36523                 Roo.get(document).on("click", this.slideInIf, this);
36524                 this.fireEvent("slideshow", this);
36525             },
36526             scope: this,
36527             block: true
36528         });
36529     },
36530
36531     afterSlideIn : function(){
36532         this.clearAutoHide();
36533         this.isSlid = false;
36534         this.clearMonitor();
36535         this.el.setStyle("z-index", "");
36536         if(this.collapseBtn){
36537             this.collapseBtn.show();
36538         }
36539         this.closeBtn.setStyle('display', this.closeBtnState);
36540         if(this.stickBtn){
36541             this.stickBtn.hide();
36542         }
36543         this.fireEvent("slidehide", this);
36544     },
36545
36546     slideIn : function(cb){
36547         if(!this.isSlid || this.el.hasActiveFx()){
36548             Roo.callback(cb);
36549             return;
36550         }
36551         this.isSlid = false;
36552         this.beforeSlide();
36553         this.el.slideOut(this.getSlideAnchor(), {
36554             callback: function(){
36555                 this.el.setLeftTop(-10000, -10000);
36556                 this.afterSlide();
36557                 this.afterSlideIn();
36558                 Roo.callback(cb);
36559             },
36560             scope: this,
36561             block: true
36562         });
36563     },
36564     
36565     slideInIf : function(e){
36566         if(!e.within(this.el)){
36567             this.slideIn();
36568         }
36569     },
36570
36571     animateCollapse : function(){
36572         this.beforeSlide();
36573         this.el.setStyle("z-index", 20000);
36574         var anchor = this.getSlideAnchor();
36575         this.el.slideOut(anchor, {
36576             callback : function(){
36577                 this.el.setStyle("z-index", "");
36578                 this.collapsedEl.slideIn(anchor, {duration:.3});
36579                 this.afterSlide();
36580                 this.el.setLocation(-10000,-10000);
36581                 this.el.hide();
36582                 this.fireEvent("collapsed", this);
36583             },
36584             scope: this,
36585             block: true
36586         });
36587     },
36588
36589     animateExpand : function(){
36590         this.beforeSlide();
36591         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36592         this.el.setStyle("z-index", 20000);
36593         this.collapsedEl.hide({
36594             duration:.1
36595         });
36596         this.el.slideIn(this.getSlideAnchor(), {
36597             callback : function(){
36598                 this.el.setStyle("z-index", "");
36599                 this.afterSlide();
36600                 if(this.split){
36601                     this.split.el.show();
36602                 }
36603                 this.fireEvent("invalidated", this);
36604                 this.fireEvent("expanded", this);
36605             },
36606             scope: this,
36607             block: true
36608         });
36609     },
36610
36611     anchors : {
36612         "west" : "left",
36613         "east" : "right",
36614         "north" : "top",
36615         "south" : "bottom"
36616     },
36617
36618     sanchors : {
36619         "west" : "l",
36620         "east" : "r",
36621         "north" : "t",
36622         "south" : "b"
36623     },
36624
36625     canchors : {
36626         "west" : "tl-tr",
36627         "east" : "tr-tl",
36628         "north" : "tl-bl",
36629         "south" : "bl-tl"
36630     },
36631
36632     getAnchor : function(){
36633         return this.anchors[this.position];
36634     },
36635
36636     getCollapseAnchor : function(){
36637         return this.canchors[this.position];
36638     },
36639
36640     getSlideAnchor : function(){
36641         return this.sanchors[this.position];
36642     },
36643
36644     getAlignAdj : function(){
36645         var cm = this.cmargins;
36646         switch(this.position){
36647             case "west":
36648                 return [0, 0];
36649             break;
36650             case "east":
36651                 return [0, 0];
36652             break;
36653             case "north":
36654                 return [0, 0];
36655             break;
36656             case "south":
36657                 return [0, 0];
36658             break;
36659         }
36660     },
36661
36662     getExpandAdj : function(){
36663         var c = this.collapsedEl, cm = this.cmargins;
36664         switch(this.position){
36665             case "west":
36666                 return [-(cm.right+c.getWidth()+cm.left), 0];
36667             break;
36668             case "east":
36669                 return [cm.right+c.getWidth()+cm.left, 0];
36670             break;
36671             case "north":
36672                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36673             break;
36674             case "south":
36675                 return [0, cm.top+cm.bottom+c.getHeight()];
36676             break;
36677         }
36678     }
36679 });/*
36680  * Based on:
36681  * Ext JS Library 1.1.1
36682  * Copyright(c) 2006-2007, Ext JS, LLC.
36683  *
36684  * Originally Released Under LGPL - original licence link has changed is not relivant.
36685  *
36686  * Fork - LGPL
36687  * <script type="text/javascript">
36688  */
36689 /*
36690  * These classes are private internal classes
36691  */
36692 Roo.bootstrap.layout.Center = function(config){
36693     config.region = "center";
36694     Roo.bootstrap.layout.Region.call(this, config);
36695     this.visible = true;
36696     this.minWidth = config.minWidth || 20;
36697     this.minHeight = config.minHeight || 20;
36698 };
36699
36700 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36701     hide : function(){
36702         // center panel can't be hidden
36703     },
36704     
36705     show : function(){
36706         // center panel can't be hidden
36707     },
36708     
36709     getMinWidth: function(){
36710         return this.minWidth;
36711     },
36712     
36713     getMinHeight: function(){
36714         return this.minHeight;
36715     }
36716 });
36717
36718
36719
36720
36721  
36722
36723
36724
36725
36726
36727 Roo.bootstrap.layout.North = function(config)
36728 {
36729     config.region = 'north';
36730     config.cursor = 'n-resize';
36731     
36732     Roo.bootstrap.layout.Split.call(this, config);
36733     
36734     
36735     if(this.split){
36736         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36737         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36738         this.split.el.addClass("roo-layout-split-v");
36739     }
36740     var size = config.initialSize || config.height;
36741     if(typeof size != "undefined"){
36742         this.el.setHeight(size);
36743     }
36744 };
36745 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36746 {
36747     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36748     
36749     
36750     
36751     getBox : function(){
36752         if(this.collapsed){
36753             return this.collapsedEl.getBox();
36754         }
36755         var box = this.el.getBox();
36756         if(this.split){
36757             box.height += this.split.el.getHeight();
36758         }
36759         return box;
36760     },
36761     
36762     updateBox : function(box){
36763         if(this.split && !this.collapsed){
36764             box.height -= this.split.el.getHeight();
36765             this.split.el.setLeft(box.x);
36766             this.split.el.setTop(box.y+box.height);
36767             this.split.el.setWidth(box.width);
36768         }
36769         if(this.collapsed){
36770             this.updateBody(box.width, null);
36771         }
36772         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36773     }
36774 });
36775
36776
36777
36778
36779
36780 Roo.bootstrap.layout.South = function(config){
36781     config.region = 'south';
36782     config.cursor = 's-resize';
36783     Roo.bootstrap.layout.Split.call(this, config);
36784     if(this.split){
36785         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36786         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36787         this.split.el.addClass("roo-layout-split-v");
36788     }
36789     var size = config.initialSize || config.height;
36790     if(typeof size != "undefined"){
36791         this.el.setHeight(size);
36792     }
36793 };
36794
36795 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36796     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36797     getBox : function(){
36798         if(this.collapsed){
36799             return this.collapsedEl.getBox();
36800         }
36801         var box = this.el.getBox();
36802         if(this.split){
36803             var sh = this.split.el.getHeight();
36804             box.height += sh;
36805             box.y -= sh;
36806         }
36807         return box;
36808     },
36809     
36810     updateBox : function(box){
36811         if(this.split && !this.collapsed){
36812             var sh = this.split.el.getHeight();
36813             box.height -= sh;
36814             box.y += sh;
36815             this.split.el.setLeft(box.x);
36816             this.split.el.setTop(box.y-sh);
36817             this.split.el.setWidth(box.width);
36818         }
36819         if(this.collapsed){
36820             this.updateBody(box.width, null);
36821         }
36822         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36823     }
36824 });
36825
36826 Roo.bootstrap.layout.East = function(config){
36827     config.region = "east";
36828     config.cursor = "e-resize";
36829     Roo.bootstrap.layout.Split.call(this, config);
36830     if(this.split){
36831         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36832         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36833         this.split.el.addClass("roo-layout-split-h");
36834     }
36835     var size = config.initialSize || config.width;
36836     if(typeof size != "undefined"){
36837         this.el.setWidth(size);
36838     }
36839 };
36840 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36841     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36842     getBox : function(){
36843         if(this.collapsed){
36844             return this.collapsedEl.getBox();
36845         }
36846         var box = this.el.getBox();
36847         if(this.split){
36848             var sw = this.split.el.getWidth();
36849             box.width += sw;
36850             box.x -= sw;
36851         }
36852         return box;
36853     },
36854
36855     updateBox : function(box){
36856         if(this.split && !this.collapsed){
36857             var sw = this.split.el.getWidth();
36858             box.width -= sw;
36859             this.split.el.setLeft(box.x);
36860             this.split.el.setTop(box.y);
36861             this.split.el.setHeight(box.height);
36862             box.x += sw;
36863         }
36864         if(this.collapsed){
36865             this.updateBody(null, box.height);
36866         }
36867         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36868     }
36869 });
36870
36871 Roo.bootstrap.layout.West = function(config){
36872     config.region = "west";
36873     config.cursor = "w-resize";
36874     
36875     Roo.bootstrap.layout.Split.call(this, config);
36876     if(this.split){
36877         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36878         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36879         this.split.el.addClass("roo-layout-split-h");
36880     }
36881     
36882 };
36883 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36884     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36885     
36886     onRender: function(ctr, pos)
36887     {
36888         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36889         var size = this.config.initialSize || this.config.width;
36890         if(typeof size != "undefined"){
36891             this.el.setWidth(size);
36892         }
36893     },
36894     
36895     getBox : function(){
36896         if(this.collapsed){
36897             return this.collapsedEl.getBox();
36898         }
36899         var box = this.el.getBox();
36900         if(this.split){
36901             box.width += this.split.el.getWidth();
36902         }
36903         return box;
36904     },
36905     
36906     updateBox : function(box){
36907         if(this.split && !this.collapsed){
36908             var sw = this.split.el.getWidth();
36909             box.width -= sw;
36910             this.split.el.setLeft(box.x+box.width);
36911             this.split.el.setTop(box.y);
36912             this.split.el.setHeight(box.height);
36913         }
36914         if(this.collapsed){
36915             this.updateBody(null, box.height);
36916         }
36917         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36918     }
36919 });
36920 Roo.namespace("Roo.bootstrap.panel");/*
36921  * Based on:
36922  * Ext JS Library 1.1.1
36923  * Copyright(c) 2006-2007, Ext JS, LLC.
36924  *
36925  * Originally Released Under LGPL - original licence link has changed is not relivant.
36926  *
36927  * Fork - LGPL
36928  * <script type="text/javascript">
36929  */
36930 /**
36931  * @class Roo.ContentPanel
36932  * @extends Roo.util.Observable
36933  * A basic ContentPanel element.
36934  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36935  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36936  * @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
36937  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36938  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36939  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36940  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36941  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36942  * @cfg {String} title          The title for this panel
36943  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36944  * @cfg {String} url            Calls {@link #setUrl} with this value
36945  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36946  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36947  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36948  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36949  * @cfg {Boolean} badges render the badges
36950
36951  * @constructor
36952  * Create a new ContentPanel.
36953  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36954  * @param {String/Object} config A string to set only the title or a config object
36955  * @param {String} content (optional) Set the HTML content for this panel
36956  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36957  */
36958 Roo.bootstrap.panel.Content = function( config){
36959     
36960     this.tpl = config.tpl || false;
36961     
36962     var el = config.el;
36963     var content = config.content;
36964
36965     if(config.autoCreate){ // xtype is available if this is called from factory
36966         el = Roo.id();
36967     }
36968     this.el = Roo.get(el);
36969     if(!this.el && config && config.autoCreate){
36970         if(typeof config.autoCreate == "object"){
36971             if(!config.autoCreate.id){
36972                 config.autoCreate.id = config.id||el;
36973             }
36974             this.el = Roo.DomHelper.append(document.body,
36975                         config.autoCreate, true);
36976         }else{
36977             var elcfg =  {   tag: "div",
36978                             cls: "roo-layout-inactive-content",
36979                             id: config.id||el
36980                             };
36981             if (config.html) {
36982                 elcfg.html = config.html;
36983                 
36984             }
36985                         
36986             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36987         }
36988     } 
36989     this.closable = false;
36990     this.loaded = false;
36991     this.active = false;
36992    
36993       
36994     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36995         
36996         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36997         
36998         this.wrapEl = this.el; //this.el.wrap();
36999         var ti = [];
37000         if (config.toolbar.items) {
37001             ti = config.toolbar.items ;
37002             delete config.toolbar.items ;
37003         }
37004         
37005         var nitems = [];
37006         this.toolbar.render(this.wrapEl, 'before');
37007         for(var i =0;i < ti.length;i++) {
37008           //  Roo.log(['add child', items[i]]);
37009             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37010         }
37011         this.toolbar.items = nitems;
37012         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37013         delete config.toolbar;
37014         
37015     }
37016     /*
37017     // xtype created footer. - not sure if will work as we normally have to render first..
37018     if (this.footer && !this.footer.el && this.footer.xtype) {
37019         if (!this.wrapEl) {
37020             this.wrapEl = this.el.wrap();
37021         }
37022     
37023         this.footer.container = this.wrapEl.createChild();
37024          
37025         this.footer = Roo.factory(this.footer, Roo);
37026         
37027     }
37028     */
37029     
37030      if(typeof config == "string"){
37031         this.title = config;
37032     }else{
37033         Roo.apply(this, config);
37034     }
37035     
37036     if(this.resizeEl){
37037         this.resizeEl = Roo.get(this.resizeEl, true);
37038     }else{
37039         this.resizeEl = this.el;
37040     }
37041     // handle view.xtype
37042     
37043  
37044     
37045     
37046     this.addEvents({
37047         /**
37048          * @event activate
37049          * Fires when this panel is activated. 
37050          * @param {Roo.ContentPanel} this
37051          */
37052         "activate" : true,
37053         /**
37054          * @event deactivate
37055          * Fires when this panel is activated. 
37056          * @param {Roo.ContentPanel} this
37057          */
37058         "deactivate" : true,
37059
37060         /**
37061          * @event resize
37062          * Fires when this panel is resized if fitToFrame is true.
37063          * @param {Roo.ContentPanel} this
37064          * @param {Number} width The width after any component adjustments
37065          * @param {Number} height The height after any component adjustments
37066          */
37067         "resize" : true,
37068         
37069          /**
37070          * @event render
37071          * Fires when this tab is created
37072          * @param {Roo.ContentPanel} this
37073          */
37074         "render" : true
37075         
37076         
37077         
37078     });
37079     
37080
37081     
37082     
37083     if(this.autoScroll){
37084         this.resizeEl.setStyle("overflow", "auto");
37085     } else {
37086         // fix randome scrolling
37087         //this.el.on('scroll', function() {
37088         //    Roo.log('fix random scolling');
37089         //    this.scrollTo('top',0); 
37090         //});
37091     }
37092     content = content || this.content;
37093     if(content){
37094         this.setContent(content);
37095     }
37096     if(config && config.url){
37097         this.setUrl(this.url, this.params, this.loadOnce);
37098     }
37099     
37100     
37101     
37102     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37103     
37104     if (this.view && typeof(this.view.xtype) != 'undefined') {
37105         this.view.el = this.el.appendChild(document.createElement("div"));
37106         this.view = Roo.factory(this.view); 
37107         this.view.render  &&  this.view.render(false, '');  
37108     }
37109     
37110     
37111     this.fireEvent('render', this);
37112 };
37113
37114 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37115     
37116     tabTip : '',
37117     
37118     setRegion : function(region){
37119         this.region = region;
37120         this.setActiveClass(region && !this.background);
37121     },
37122     
37123     
37124     setActiveClass: function(state)
37125     {
37126         if(state){
37127            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37128            this.el.setStyle('position','relative');
37129         }else{
37130            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37131            this.el.setStyle('position', 'absolute');
37132         } 
37133     },
37134     
37135     /**
37136      * Returns the toolbar for this Panel if one was configured. 
37137      * @return {Roo.Toolbar} 
37138      */
37139     getToolbar : function(){
37140         return this.toolbar;
37141     },
37142     
37143     setActiveState : function(active)
37144     {
37145         this.active = active;
37146         this.setActiveClass(active);
37147         if(!active){
37148             if(this.fireEvent("deactivate", this) === false){
37149                 return false;
37150             }
37151             return true;
37152         }
37153         this.fireEvent("activate", this);
37154         return true;
37155     },
37156     /**
37157      * Updates this panel's element
37158      * @param {String} content The new content
37159      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37160     */
37161     setContent : function(content, loadScripts){
37162         this.el.update(content, loadScripts);
37163     },
37164
37165     ignoreResize : function(w, h){
37166         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37167             return true;
37168         }else{
37169             this.lastSize = {width: w, height: h};
37170             return false;
37171         }
37172     },
37173     /**
37174      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37175      * @return {Roo.UpdateManager} The UpdateManager
37176      */
37177     getUpdateManager : function(){
37178         return this.el.getUpdateManager();
37179     },
37180      /**
37181      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37182      * @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:
37183 <pre><code>
37184 panel.load({
37185     url: "your-url.php",
37186     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37187     callback: yourFunction,
37188     scope: yourObject, //(optional scope)
37189     discardUrl: false,
37190     nocache: false,
37191     text: "Loading...",
37192     timeout: 30,
37193     scripts: false
37194 });
37195 </code></pre>
37196      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37197      * 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.
37198      * @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}
37199      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37200      * @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.
37201      * @return {Roo.ContentPanel} this
37202      */
37203     load : function(){
37204         var um = this.el.getUpdateManager();
37205         um.update.apply(um, arguments);
37206         return this;
37207     },
37208
37209
37210     /**
37211      * 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.
37212      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37213      * @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)
37214      * @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)
37215      * @return {Roo.UpdateManager} The UpdateManager
37216      */
37217     setUrl : function(url, params, loadOnce){
37218         if(this.refreshDelegate){
37219             this.removeListener("activate", this.refreshDelegate);
37220         }
37221         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37222         this.on("activate", this.refreshDelegate);
37223         return this.el.getUpdateManager();
37224     },
37225     
37226     _handleRefresh : function(url, params, loadOnce){
37227         if(!loadOnce || !this.loaded){
37228             var updater = this.el.getUpdateManager();
37229             updater.update(url, params, this._setLoaded.createDelegate(this));
37230         }
37231     },
37232     
37233     _setLoaded : function(){
37234         this.loaded = true;
37235     }, 
37236     
37237     /**
37238      * Returns this panel's id
37239      * @return {String} 
37240      */
37241     getId : function(){
37242         return this.el.id;
37243     },
37244     
37245     /** 
37246      * Returns this panel's element - used by regiosn to add.
37247      * @return {Roo.Element} 
37248      */
37249     getEl : function(){
37250         return this.wrapEl || this.el;
37251     },
37252     
37253    
37254     
37255     adjustForComponents : function(width, height)
37256     {
37257         //Roo.log('adjustForComponents ');
37258         if(this.resizeEl != this.el){
37259             width -= this.el.getFrameWidth('lr');
37260             height -= this.el.getFrameWidth('tb');
37261         }
37262         if(this.toolbar){
37263             var te = this.toolbar.getEl();
37264             te.setWidth(width);
37265             height -= te.getHeight();
37266         }
37267         if(this.footer){
37268             var te = this.footer.getEl();
37269             te.setWidth(width);
37270             height -= te.getHeight();
37271         }
37272         
37273         
37274         if(this.adjustments){
37275             width += this.adjustments[0];
37276             height += this.adjustments[1];
37277         }
37278         return {"width": width, "height": height};
37279     },
37280     
37281     setSize : function(width, height){
37282         if(this.fitToFrame && !this.ignoreResize(width, height)){
37283             if(this.fitContainer && this.resizeEl != this.el){
37284                 this.el.setSize(width, height);
37285             }
37286             var size = this.adjustForComponents(width, height);
37287             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37288             this.fireEvent('resize', this, size.width, size.height);
37289         }
37290     },
37291     
37292     /**
37293      * Returns this panel's title
37294      * @return {String} 
37295      */
37296     getTitle : function(){
37297         
37298         if (typeof(this.title) != 'object') {
37299             return this.title;
37300         }
37301         
37302         var t = '';
37303         for (var k in this.title) {
37304             if (!this.title.hasOwnProperty(k)) {
37305                 continue;
37306             }
37307             
37308             if (k.indexOf('-') >= 0) {
37309                 var s = k.split('-');
37310                 for (var i = 0; i<s.length; i++) {
37311                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37312                 }
37313             } else {
37314                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37315             }
37316         }
37317         return t;
37318     },
37319     
37320     /**
37321      * Set this panel's title
37322      * @param {String} title
37323      */
37324     setTitle : function(title){
37325         this.title = title;
37326         if(this.region){
37327             this.region.updatePanelTitle(this, title);
37328         }
37329     },
37330     
37331     /**
37332      * Returns true is this panel was configured to be closable
37333      * @return {Boolean} 
37334      */
37335     isClosable : function(){
37336         return this.closable;
37337     },
37338     
37339     beforeSlide : function(){
37340         this.el.clip();
37341         this.resizeEl.clip();
37342     },
37343     
37344     afterSlide : function(){
37345         this.el.unclip();
37346         this.resizeEl.unclip();
37347     },
37348     
37349     /**
37350      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37351      *   Will fail silently if the {@link #setUrl} method has not been called.
37352      *   This does not activate the panel, just updates its content.
37353      */
37354     refresh : function(){
37355         if(this.refreshDelegate){
37356            this.loaded = false;
37357            this.refreshDelegate();
37358         }
37359     },
37360     
37361     /**
37362      * Destroys this panel
37363      */
37364     destroy : function(){
37365         this.el.removeAllListeners();
37366         var tempEl = document.createElement("span");
37367         tempEl.appendChild(this.el.dom);
37368         tempEl.innerHTML = "";
37369         this.el.remove();
37370         this.el = null;
37371     },
37372     
37373     /**
37374      * form - if the content panel contains a form - this is a reference to it.
37375      * @type {Roo.form.Form}
37376      */
37377     form : false,
37378     /**
37379      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37380      *    This contains a reference to it.
37381      * @type {Roo.View}
37382      */
37383     view : false,
37384     
37385       /**
37386      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37387      * <pre><code>
37388
37389 layout.addxtype({
37390        xtype : 'Form',
37391        items: [ .... ]
37392    }
37393 );
37394
37395 </code></pre>
37396      * @param {Object} cfg Xtype definition of item to add.
37397      */
37398     
37399     
37400     getChildContainer: function () {
37401         return this.getEl();
37402     }
37403     
37404     
37405     /*
37406         var  ret = new Roo.factory(cfg);
37407         return ret;
37408         
37409         
37410         // add form..
37411         if (cfg.xtype.match(/^Form$/)) {
37412             
37413             var el;
37414             //if (this.footer) {
37415             //    el = this.footer.container.insertSibling(false, 'before');
37416             //} else {
37417                 el = this.el.createChild();
37418             //}
37419
37420             this.form = new  Roo.form.Form(cfg);
37421             
37422             
37423             if ( this.form.allItems.length) {
37424                 this.form.render(el.dom);
37425             }
37426             return this.form;
37427         }
37428         // should only have one of theses..
37429         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37430             // views.. should not be just added - used named prop 'view''
37431             
37432             cfg.el = this.el.appendChild(document.createElement("div"));
37433             // factory?
37434             
37435             var ret = new Roo.factory(cfg);
37436              
37437              ret.render && ret.render(false, ''); // render blank..
37438             this.view = ret;
37439             return ret;
37440         }
37441         return false;
37442     }
37443     \*/
37444 });
37445  
37446 /**
37447  * @class Roo.bootstrap.panel.Grid
37448  * @extends Roo.bootstrap.panel.Content
37449  * @constructor
37450  * Create a new GridPanel.
37451  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37452  * @param {Object} config A the config object
37453   
37454  */
37455
37456
37457
37458 Roo.bootstrap.panel.Grid = function(config)
37459 {
37460     
37461       
37462     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37463         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37464
37465     config.el = this.wrapper;
37466     //this.el = this.wrapper;
37467     
37468       if (config.container) {
37469         // ctor'ed from a Border/panel.grid
37470         
37471         
37472         this.wrapper.setStyle("overflow", "hidden");
37473         this.wrapper.addClass('roo-grid-container');
37474
37475     }
37476     
37477     
37478     if(config.toolbar){
37479         var tool_el = this.wrapper.createChild();    
37480         this.toolbar = Roo.factory(config.toolbar);
37481         var ti = [];
37482         if (config.toolbar.items) {
37483             ti = config.toolbar.items ;
37484             delete config.toolbar.items ;
37485         }
37486         
37487         var nitems = [];
37488         this.toolbar.render(tool_el);
37489         for(var i =0;i < ti.length;i++) {
37490           //  Roo.log(['add child', items[i]]);
37491             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37492         }
37493         this.toolbar.items = nitems;
37494         
37495         delete config.toolbar;
37496     }
37497     
37498     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37499     config.grid.scrollBody = true;;
37500     config.grid.monitorWindowResize = false; // turn off autosizing
37501     config.grid.autoHeight = false;
37502     config.grid.autoWidth = false;
37503     
37504     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37505     
37506     if (config.background) {
37507         // render grid on panel activation (if panel background)
37508         this.on('activate', function(gp) {
37509             if (!gp.grid.rendered) {
37510                 gp.grid.render(this.wrapper);
37511                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37512             }
37513         });
37514             
37515     } else {
37516         this.grid.render(this.wrapper);
37517         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37518
37519     }
37520     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37521     // ??? needed ??? config.el = this.wrapper;
37522     
37523     
37524     
37525   
37526     // xtype created footer. - not sure if will work as we normally have to render first..
37527     if (this.footer && !this.footer.el && this.footer.xtype) {
37528         
37529         var ctr = this.grid.getView().getFooterPanel(true);
37530         this.footer.dataSource = this.grid.dataSource;
37531         this.footer = Roo.factory(this.footer, Roo);
37532         this.footer.render(ctr);
37533         
37534     }
37535     
37536     
37537     
37538     
37539      
37540 };
37541
37542 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37543     getId : function(){
37544         return this.grid.id;
37545     },
37546     
37547     /**
37548      * Returns the grid for this panel
37549      * @return {Roo.bootstrap.Table} 
37550      */
37551     getGrid : function(){
37552         return this.grid;    
37553     },
37554     
37555     setSize : function(width, height){
37556         if(!this.ignoreResize(width, height)){
37557             var grid = this.grid;
37558             var size = this.adjustForComponents(width, height);
37559             var gridel = grid.getGridEl();
37560             gridel.setSize(size.width, size.height);
37561             /*
37562             var thd = grid.getGridEl().select('thead',true).first();
37563             var tbd = grid.getGridEl().select('tbody', true).first();
37564             if (tbd) {
37565                 tbd.setSize(width, height - thd.getHeight());
37566             }
37567             */
37568             grid.autoSize();
37569         }
37570     },
37571      
37572     
37573     
37574     beforeSlide : function(){
37575         this.grid.getView().scroller.clip();
37576     },
37577     
37578     afterSlide : function(){
37579         this.grid.getView().scroller.unclip();
37580     },
37581     
37582     destroy : function(){
37583         this.grid.destroy();
37584         delete this.grid;
37585         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37586     }
37587 });
37588
37589 /**
37590  * @class Roo.bootstrap.panel.Nest
37591  * @extends Roo.bootstrap.panel.Content
37592  * @constructor
37593  * Create a new Panel, that can contain a layout.Border.
37594  * 
37595  * 
37596  * @param {Roo.BorderLayout} layout The layout for this panel
37597  * @param {String/Object} config A string to set only the title or a config object
37598  */
37599 Roo.bootstrap.panel.Nest = function(config)
37600 {
37601     // construct with only one argument..
37602     /* FIXME - implement nicer consturctors
37603     if (layout.layout) {
37604         config = layout;
37605         layout = config.layout;
37606         delete config.layout;
37607     }
37608     if (layout.xtype && !layout.getEl) {
37609         // then layout needs constructing..
37610         layout = Roo.factory(layout, Roo);
37611     }
37612     */
37613     
37614     config.el =  config.layout.getEl();
37615     
37616     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37617     
37618     config.layout.monitorWindowResize = false; // turn off autosizing
37619     this.layout = config.layout;
37620     this.layout.getEl().addClass("roo-layout-nested-layout");
37621     
37622     
37623     
37624     
37625 };
37626
37627 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37628
37629     setSize : function(width, height){
37630         if(!this.ignoreResize(width, height)){
37631             var size = this.adjustForComponents(width, height);
37632             var el = this.layout.getEl();
37633             if (size.height < 1) {
37634                 el.setWidth(size.width);   
37635             } else {
37636                 el.setSize(size.width, size.height);
37637             }
37638             var touch = el.dom.offsetWidth;
37639             this.layout.layout();
37640             // ie requires a double layout on the first pass
37641             if(Roo.isIE && !this.initialized){
37642                 this.initialized = true;
37643                 this.layout.layout();
37644             }
37645         }
37646     },
37647     
37648     // activate all subpanels if not currently active..
37649     
37650     setActiveState : function(active){
37651         this.active = active;
37652         this.setActiveClass(active);
37653         
37654         if(!active){
37655             this.fireEvent("deactivate", this);
37656             return;
37657         }
37658         
37659         this.fireEvent("activate", this);
37660         // not sure if this should happen before or after..
37661         if (!this.layout) {
37662             return; // should not happen..
37663         }
37664         var reg = false;
37665         for (var r in this.layout.regions) {
37666             reg = this.layout.getRegion(r);
37667             if (reg.getActivePanel()) {
37668                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37669                 reg.setActivePanel(reg.getActivePanel());
37670                 continue;
37671             }
37672             if (!reg.panels.length) {
37673                 continue;
37674             }
37675             reg.showPanel(reg.getPanel(0));
37676         }
37677         
37678         
37679         
37680         
37681     },
37682     
37683     /**
37684      * Returns the nested BorderLayout for this panel
37685      * @return {Roo.BorderLayout} 
37686      */
37687     getLayout : function(){
37688         return this.layout;
37689     },
37690     
37691      /**
37692      * Adds a xtype elements to the layout of the nested panel
37693      * <pre><code>
37694
37695 panel.addxtype({
37696        xtype : 'ContentPanel',
37697        region: 'west',
37698        items: [ .... ]
37699    }
37700 );
37701
37702 panel.addxtype({
37703         xtype : 'NestedLayoutPanel',
37704         region: 'west',
37705         layout: {
37706            center: { },
37707            west: { }   
37708         },
37709         items : [ ... list of content panels or nested layout panels.. ]
37710    }
37711 );
37712 </code></pre>
37713      * @param {Object} cfg Xtype definition of item to add.
37714      */
37715     addxtype : function(cfg) {
37716         return this.layout.addxtype(cfg);
37717     
37718     }
37719 });        /*
37720  * Based on:
37721  * Ext JS Library 1.1.1
37722  * Copyright(c) 2006-2007, Ext JS, LLC.
37723  *
37724  * Originally Released Under LGPL - original licence link has changed is not relivant.
37725  *
37726  * Fork - LGPL
37727  * <script type="text/javascript">
37728  */
37729 /**
37730  * @class Roo.TabPanel
37731  * @extends Roo.util.Observable
37732  * A lightweight tab container.
37733  * <br><br>
37734  * Usage:
37735  * <pre><code>
37736 // basic tabs 1, built from existing content
37737 var tabs = new Roo.TabPanel("tabs1");
37738 tabs.addTab("script", "View Script");
37739 tabs.addTab("markup", "View Markup");
37740 tabs.activate("script");
37741
37742 // more advanced tabs, built from javascript
37743 var jtabs = new Roo.TabPanel("jtabs");
37744 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37745
37746 // set up the UpdateManager
37747 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37748 var updater = tab2.getUpdateManager();
37749 updater.setDefaultUrl("ajax1.htm");
37750 tab2.on('activate', updater.refresh, updater, true);
37751
37752 // Use setUrl for Ajax loading
37753 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37754 tab3.setUrl("ajax2.htm", null, true);
37755
37756 // Disabled tab
37757 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37758 tab4.disable();
37759
37760 jtabs.activate("jtabs-1");
37761  * </code></pre>
37762  * @constructor
37763  * Create a new TabPanel.
37764  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37765  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37766  */
37767 Roo.bootstrap.panel.Tabs = function(config){
37768     /**
37769     * The container element for this TabPanel.
37770     * @type Roo.Element
37771     */
37772     this.el = Roo.get(config.el);
37773     delete config.el;
37774     if(config){
37775         if(typeof config == "boolean"){
37776             this.tabPosition = config ? "bottom" : "top";
37777         }else{
37778             Roo.apply(this, config);
37779         }
37780     }
37781     
37782     if(this.tabPosition == "bottom"){
37783         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37784         this.el.addClass("roo-tabs-bottom");
37785     }
37786     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37787     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37788     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37789     if(Roo.isIE){
37790         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37791     }
37792     if(this.tabPosition != "bottom"){
37793         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37794          * @type Roo.Element
37795          */
37796         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37797         this.el.addClass("roo-tabs-top");
37798     }
37799     this.items = [];
37800
37801     this.bodyEl.setStyle("position", "relative");
37802
37803     this.active = null;
37804     this.activateDelegate = this.activate.createDelegate(this);
37805
37806     this.addEvents({
37807         /**
37808          * @event tabchange
37809          * Fires when the active tab changes
37810          * @param {Roo.TabPanel} this
37811          * @param {Roo.TabPanelItem} activePanel The new active tab
37812          */
37813         "tabchange": true,
37814         /**
37815          * @event beforetabchange
37816          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37817          * @param {Roo.TabPanel} this
37818          * @param {Object} e Set cancel to true on this object to cancel the tab change
37819          * @param {Roo.TabPanelItem} tab The tab being changed to
37820          */
37821         "beforetabchange" : true
37822     });
37823
37824     Roo.EventManager.onWindowResize(this.onResize, this);
37825     this.cpad = this.el.getPadding("lr");
37826     this.hiddenCount = 0;
37827
37828
37829     // toolbar on the tabbar support...
37830     if (this.toolbar) {
37831         alert("no toolbar support yet");
37832         this.toolbar  = false;
37833         /*
37834         var tcfg = this.toolbar;
37835         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37836         this.toolbar = new Roo.Toolbar(tcfg);
37837         if (Roo.isSafari) {
37838             var tbl = tcfg.container.child('table', true);
37839             tbl.setAttribute('width', '100%');
37840         }
37841         */
37842         
37843     }
37844    
37845
37846
37847     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37848 };
37849
37850 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37851     /*
37852      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37853      */
37854     tabPosition : "top",
37855     /*
37856      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37857      */
37858     currentTabWidth : 0,
37859     /*
37860      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37861      */
37862     minTabWidth : 40,
37863     /*
37864      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37865      */
37866     maxTabWidth : 250,
37867     /*
37868      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37869      */
37870     preferredTabWidth : 175,
37871     /*
37872      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37873      */
37874     resizeTabs : false,
37875     /*
37876      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37877      */
37878     monitorResize : true,
37879     /*
37880      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37881      */
37882     toolbar : false,
37883
37884     /**
37885      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37886      * @param {String} id The id of the div to use <b>or create</b>
37887      * @param {String} text The text for the tab
37888      * @param {String} content (optional) Content to put in the TabPanelItem body
37889      * @param {Boolean} closable (optional) True to create a close icon on the tab
37890      * @return {Roo.TabPanelItem} The created TabPanelItem
37891      */
37892     addTab : function(id, text, content, closable, tpl)
37893     {
37894         var item = new Roo.bootstrap.panel.TabItem({
37895             panel: this,
37896             id : id,
37897             text : text,
37898             closable : closable,
37899             tpl : tpl
37900         });
37901         this.addTabItem(item);
37902         if(content){
37903             item.setContent(content);
37904         }
37905         return item;
37906     },
37907
37908     /**
37909      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37910      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37911      * @return {Roo.TabPanelItem}
37912      */
37913     getTab : function(id){
37914         return this.items[id];
37915     },
37916
37917     /**
37918      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37919      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37920      */
37921     hideTab : function(id){
37922         var t = this.items[id];
37923         if(!t.isHidden()){
37924            t.setHidden(true);
37925            this.hiddenCount++;
37926            this.autoSizeTabs();
37927         }
37928     },
37929
37930     /**
37931      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37932      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37933      */
37934     unhideTab : function(id){
37935         var t = this.items[id];
37936         if(t.isHidden()){
37937            t.setHidden(false);
37938            this.hiddenCount--;
37939            this.autoSizeTabs();
37940         }
37941     },
37942
37943     /**
37944      * Adds an existing {@link Roo.TabPanelItem}.
37945      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37946      */
37947     addTabItem : function(item){
37948         this.items[item.id] = item;
37949         this.items.push(item);
37950       //  if(this.resizeTabs){
37951     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37952   //         this.autoSizeTabs();
37953 //        }else{
37954 //            item.autoSize();
37955        // }
37956     },
37957
37958     /**
37959      * Removes a {@link Roo.TabPanelItem}.
37960      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37961      */
37962     removeTab : function(id){
37963         var items = this.items;
37964         var tab = items[id];
37965         if(!tab) { return; }
37966         var index = items.indexOf(tab);
37967         if(this.active == tab && items.length > 1){
37968             var newTab = this.getNextAvailable(index);
37969             if(newTab) {
37970                 newTab.activate();
37971             }
37972         }
37973         this.stripEl.dom.removeChild(tab.pnode.dom);
37974         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37975             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37976         }
37977         items.splice(index, 1);
37978         delete this.items[tab.id];
37979         tab.fireEvent("close", tab);
37980         tab.purgeListeners();
37981         this.autoSizeTabs();
37982     },
37983
37984     getNextAvailable : function(start){
37985         var items = this.items;
37986         var index = start;
37987         // look for a next tab that will slide over to
37988         // replace the one being removed
37989         while(index < items.length){
37990             var item = items[++index];
37991             if(item && !item.isHidden()){
37992                 return item;
37993             }
37994         }
37995         // if one isn't found select the previous tab (on the left)
37996         index = start;
37997         while(index >= 0){
37998             var item = items[--index];
37999             if(item && !item.isHidden()){
38000                 return item;
38001             }
38002         }
38003         return null;
38004     },
38005
38006     /**
38007      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38008      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38009      */
38010     disableTab : function(id){
38011         var tab = this.items[id];
38012         if(tab && this.active != tab){
38013             tab.disable();
38014         }
38015     },
38016
38017     /**
38018      * Enables a {@link Roo.TabPanelItem} that is disabled.
38019      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38020      */
38021     enableTab : function(id){
38022         var tab = this.items[id];
38023         tab.enable();
38024     },
38025
38026     /**
38027      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38028      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38029      * @return {Roo.TabPanelItem} The TabPanelItem.
38030      */
38031     activate : function(id){
38032         var tab = this.items[id];
38033         if(!tab){
38034             return null;
38035         }
38036         if(tab == this.active || tab.disabled){
38037             return tab;
38038         }
38039         var e = {};
38040         this.fireEvent("beforetabchange", this, e, tab);
38041         if(e.cancel !== true && !tab.disabled){
38042             if(this.active){
38043                 this.active.hide();
38044             }
38045             this.active = this.items[id];
38046             this.active.show();
38047             this.fireEvent("tabchange", this, this.active);
38048         }
38049         return tab;
38050     },
38051
38052     /**
38053      * Gets the active {@link Roo.TabPanelItem}.
38054      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38055      */
38056     getActiveTab : function(){
38057         return this.active;
38058     },
38059
38060     /**
38061      * Updates the tab body element to fit the height of the container element
38062      * for overflow scrolling
38063      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38064      */
38065     syncHeight : function(targetHeight){
38066         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38067         var bm = this.bodyEl.getMargins();
38068         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38069         this.bodyEl.setHeight(newHeight);
38070         return newHeight;
38071     },
38072
38073     onResize : function(){
38074         if(this.monitorResize){
38075             this.autoSizeTabs();
38076         }
38077     },
38078
38079     /**
38080      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38081      */
38082     beginUpdate : function(){
38083         this.updating = true;
38084     },
38085
38086     /**
38087      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38088      */
38089     endUpdate : function(){
38090         this.updating = false;
38091         this.autoSizeTabs();
38092     },
38093
38094     /**
38095      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38096      */
38097     autoSizeTabs : function(){
38098         var count = this.items.length;
38099         var vcount = count - this.hiddenCount;
38100         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38101             return;
38102         }
38103         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38104         var availWidth = Math.floor(w / vcount);
38105         var b = this.stripBody;
38106         if(b.getWidth() > w){
38107             var tabs = this.items;
38108             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38109             if(availWidth < this.minTabWidth){
38110                 /*if(!this.sleft){    // incomplete scrolling code
38111                     this.createScrollButtons();
38112                 }
38113                 this.showScroll();
38114                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38115             }
38116         }else{
38117             if(this.currentTabWidth < this.preferredTabWidth){
38118                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38119             }
38120         }
38121     },
38122
38123     /**
38124      * Returns the number of tabs in this TabPanel.
38125      * @return {Number}
38126      */
38127      getCount : function(){
38128          return this.items.length;
38129      },
38130
38131     /**
38132      * Resizes all the tabs to the passed width
38133      * @param {Number} The new width
38134      */
38135     setTabWidth : function(width){
38136         this.currentTabWidth = width;
38137         for(var i = 0, len = this.items.length; i < len; i++) {
38138                 if(!this.items[i].isHidden()) {
38139                 this.items[i].setWidth(width);
38140             }
38141         }
38142     },
38143
38144     /**
38145      * Destroys this TabPanel
38146      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38147      */
38148     destroy : function(removeEl){
38149         Roo.EventManager.removeResizeListener(this.onResize, this);
38150         for(var i = 0, len = this.items.length; i < len; i++){
38151             this.items[i].purgeListeners();
38152         }
38153         if(removeEl === true){
38154             this.el.update("");
38155             this.el.remove();
38156         }
38157     },
38158     
38159     createStrip : function(container)
38160     {
38161         var strip = document.createElement("nav");
38162         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38163         container.appendChild(strip);
38164         return strip;
38165     },
38166     
38167     createStripList : function(strip)
38168     {
38169         // div wrapper for retard IE
38170         // returns the "tr" element.
38171         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38172         //'<div class="x-tabs-strip-wrap">'+
38173           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38174           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38175         return strip.firstChild; //.firstChild.firstChild.firstChild;
38176     },
38177     createBody : function(container)
38178     {
38179         var body = document.createElement("div");
38180         Roo.id(body, "tab-body");
38181         //Roo.fly(body).addClass("x-tabs-body");
38182         Roo.fly(body).addClass("tab-content");
38183         container.appendChild(body);
38184         return body;
38185     },
38186     createItemBody :function(bodyEl, id){
38187         var body = Roo.getDom(id);
38188         if(!body){
38189             body = document.createElement("div");
38190             body.id = id;
38191         }
38192         //Roo.fly(body).addClass("x-tabs-item-body");
38193         Roo.fly(body).addClass("tab-pane");
38194          bodyEl.insertBefore(body, bodyEl.firstChild);
38195         return body;
38196     },
38197     /** @private */
38198     createStripElements :  function(stripEl, text, closable, tpl)
38199     {
38200         var td = document.createElement("li"); // was td..
38201         
38202         
38203         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38204         
38205         
38206         stripEl.appendChild(td);
38207         /*if(closable){
38208             td.className = "x-tabs-closable";
38209             if(!this.closeTpl){
38210                 this.closeTpl = new Roo.Template(
38211                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38212                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38213                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38214                 );
38215             }
38216             var el = this.closeTpl.overwrite(td, {"text": text});
38217             var close = el.getElementsByTagName("div")[0];
38218             var inner = el.getElementsByTagName("em")[0];
38219             return {"el": el, "close": close, "inner": inner};
38220         } else {
38221         */
38222         // not sure what this is..
38223 //            if(!this.tabTpl){
38224                 //this.tabTpl = 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></em></span></a>'
38227                 //);
38228 //                this.tabTpl = new Roo.Template(
38229 //                   '<a href="#">' +
38230 //                   '<span unselectable="on"' +
38231 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38232 //                            ' >{text}</span></a>'
38233 //                );
38234 //                
38235 //            }
38236
38237
38238             var template = tpl || this.tabTpl || false;
38239             
38240             if(!template){
38241                 
38242                 template = new Roo.Template(
38243                    '<a href="#">' +
38244                    '<span unselectable="on"' +
38245                             (this.disableTooltips ? '' : ' title="{text}"') +
38246                             ' >{text}</span></a>'
38247                 );
38248             }
38249             
38250             switch (typeof(template)) {
38251                 case 'object' :
38252                     break;
38253                 case 'string' :
38254                     template = new Roo.Template(template);
38255                     break;
38256                 default :
38257                     break;
38258             }
38259             
38260             var el = template.overwrite(td, {"text": text});
38261             
38262             var inner = el.getElementsByTagName("span")[0];
38263             
38264             return {"el": el, "inner": inner};
38265             
38266     }
38267         
38268     
38269 });
38270
38271 /**
38272  * @class Roo.TabPanelItem
38273  * @extends Roo.util.Observable
38274  * Represents an individual item (tab plus body) in a TabPanel.
38275  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38276  * @param {String} id The id of this TabPanelItem
38277  * @param {String} text The text for the tab of this TabPanelItem
38278  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38279  */
38280 Roo.bootstrap.panel.TabItem = function(config){
38281     /**
38282      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38283      * @type Roo.TabPanel
38284      */
38285     this.tabPanel = config.panel;
38286     /**
38287      * The id for this TabPanelItem
38288      * @type String
38289      */
38290     this.id = config.id;
38291     /** @private */
38292     this.disabled = false;
38293     /** @private */
38294     this.text = config.text;
38295     /** @private */
38296     this.loaded = false;
38297     this.closable = config.closable;
38298
38299     /**
38300      * The body element for this TabPanelItem.
38301      * @type Roo.Element
38302      */
38303     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38304     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38305     this.bodyEl.setStyle("display", "block");
38306     this.bodyEl.setStyle("zoom", "1");
38307     //this.hideAction();
38308
38309     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38310     /** @private */
38311     this.el = Roo.get(els.el);
38312     this.inner = Roo.get(els.inner, true);
38313     this.textEl = Roo.get(this.el.dom.firstChild, true);
38314     this.pnode = Roo.get(els.el.parentNode, true);
38315 //    this.el.on("mousedown", this.onTabMouseDown, this);
38316     this.el.on("click", this.onTabClick, this);
38317     /** @private */
38318     if(config.closable){
38319         var c = Roo.get(els.close, true);
38320         c.dom.title = this.closeText;
38321         c.addClassOnOver("close-over");
38322         c.on("click", this.closeClick, this);
38323      }
38324
38325     this.addEvents({
38326          /**
38327          * @event activate
38328          * Fires when this tab becomes the active tab.
38329          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38330          * @param {Roo.TabPanelItem} this
38331          */
38332         "activate": true,
38333         /**
38334          * @event beforeclose
38335          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38336          * @param {Roo.TabPanelItem} this
38337          * @param {Object} e Set cancel to true on this object to cancel the close.
38338          */
38339         "beforeclose": true,
38340         /**
38341          * @event close
38342          * Fires when this tab is closed.
38343          * @param {Roo.TabPanelItem} this
38344          */
38345          "close": true,
38346         /**
38347          * @event deactivate
38348          * Fires when this tab is no longer the active tab.
38349          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38350          * @param {Roo.TabPanelItem} this
38351          */
38352          "deactivate" : true
38353     });
38354     this.hidden = false;
38355
38356     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38357 };
38358
38359 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38360            {
38361     purgeListeners : function(){
38362        Roo.util.Observable.prototype.purgeListeners.call(this);
38363        this.el.removeAllListeners();
38364     },
38365     /**
38366      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38367      */
38368     show : function(){
38369         this.pnode.addClass("active");
38370         this.showAction();
38371         if(Roo.isOpera){
38372             this.tabPanel.stripWrap.repaint();
38373         }
38374         this.fireEvent("activate", this.tabPanel, this);
38375     },
38376
38377     /**
38378      * Returns true if this tab is the active tab.
38379      * @return {Boolean}
38380      */
38381     isActive : function(){
38382         return this.tabPanel.getActiveTab() == this;
38383     },
38384
38385     /**
38386      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38387      */
38388     hide : function(){
38389         this.pnode.removeClass("active");
38390         this.hideAction();
38391         this.fireEvent("deactivate", this.tabPanel, this);
38392     },
38393
38394     hideAction : function(){
38395         this.bodyEl.hide();
38396         this.bodyEl.setStyle("position", "absolute");
38397         this.bodyEl.setLeft("-20000px");
38398         this.bodyEl.setTop("-20000px");
38399     },
38400
38401     showAction : function(){
38402         this.bodyEl.setStyle("position", "relative");
38403         this.bodyEl.setTop("");
38404         this.bodyEl.setLeft("");
38405         this.bodyEl.show();
38406     },
38407
38408     /**
38409      * Set the tooltip for the tab.
38410      * @param {String} tooltip The tab's tooltip
38411      */
38412     setTooltip : function(text){
38413         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38414             this.textEl.dom.qtip = text;
38415             this.textEl.dom.removeAttribute('title');
38416         }else{
38417             this.textEl.dom.title = text;
38418         }
38419     },
38420
38421     onTabClick : function(e){
38422         e.preventDefault();
38423         this.tabPanel.activate(this.id);
38424     },
38425
38426     onTabMouseDown : function(e){
38427         e.preventDefault();
38428         this.tabPanel.activate(this.id);
38429     },
38430 /*
38431     getWidth : function(){
38432         return this.inner.getWidth();
38433     },
38434
38435     setWidth : function(width){
38436         var iwidth = width - this.pnode.getPadding("lr");
38437         this.inner.setWidth(iwidth);
38438         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38439         this.pnode.setWidth(width);
38440     },
38441 */
38442     /**
38443      * Show or hide the tab
38444      * @param {Boolean} hidden True to hide or false to show.
38445      */
38446     setHidden : function(hidden){
38447         this.hidden = hidden;
38448         this.pnode.setStyle("display", hidden ? "none" : "");
38449     },
38450
38451     /**
38452      * Returns true if this tab is "hidden"
38453      * @return {Boolean}
38454      */
38455     isHidden : function(){
38456         return this.hidden;
38457     },
38458
38459     /**
38460      * Returns the text for this tab
38461      * @return {String}
38462      */
38463     getText : function(){
38464         return this.text;
38465     },
38466     /*
38467     autoSize : function(){
38468         //this.el.beginMeasure();
38469         this.textEl.setWidth(1);
38470         /*
38471          *  #2804 [new] Tabs in Roojs
38472          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38473          */
38474         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38475         //this.el.endMeasure();
38476     //},
38477
38478     /**
38479      * Sets the text for the tab (Note: this also sets the tooltip text)
38480      * @param {String} text The tab's text and tooltip
38481      */
38482     setText : function(text){
38483         this.text = text;
38484         this.textEl.update(text);
38485         this.setTooltip(text);
38486         //if(!this.tabPanel.resizeTabs){
38487         //    this.autoSize();
38488         //}
38489     },
38490     /**
38491      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38492      */
38493     activate : function(){
38494         this.tabPanel.activate(this.id);
38495     },
38496
38497     /**
38498      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38499      */
38500     disable : function(){
38501         if(this.tabPanel.active != this){
38502             this.disabled = true;
38503             this.pnode.addClass("disabled");
38504         }
38505     },
38506
38507     /**
38508      * Enables this TabPanelItem if it was previously disabled.
38509      */
38510     enable : function(){
38511         this.disabled = false;
38512         this.pnode.removeClass("disabled");
38513     },
38514
38515     /**
38516      * Sets the content for this TabPanelItem.
38517      * @param {String} content The content
38518      * @param {Boolean} loadScripts true to look for and load scripts
38519      */
38520     setContent : function(content, loadScripts){
38521         this.bodyEl.update(content, loadScripts);
38522     },
38523
38524     /**
38525      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38526      * @return {Roo.UpdateManager} The UpdateManager
38527      */
38528     getUpdateManager : function(){
38529         return this.bodyEl.getUpdateManager();
38530     },
38531
38532     /**
38533      * Set a URL to be used to load the content for this TabPanelItem.
38534      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38535      * @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)
38536      * @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)
38537      * @return {Roo.UpdateManager} The UpdateManager
38538      */
38539     setUrl : function(url, params, loadOnce){
38540         if(this.refreshDelegate){
38541             this.un('activate', this.refreshDelegate);
38542         }
38543         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38544         this.on("activate", this.refreshDelegate);
38545         return this.bodyEl.getUpdateManager();
38546     },
38547
38548     /** @private */
38549     _handleRefresh : function(url, params, loadOnce){
38550         if(!loadOnce || !this.loaded){
38551             var updater = this.bodyEl.getUpdateManager();
38552             updater.update(url, params, this._setLoaded.createDelegate(this));
38553         }
38554     },
38555
38556     /**
38557      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38558      *   Will fail silently if the setUrl method has not been called.
38559      *   This does not activate the panel, just updates its content.
38560      */
38561     refresh : function(){
38562         if(this.refreshDelegate){
38563            this.loaded = false;
38564            this.refreshDelegate();
38565         }
38566     },
38567
38568     /** @private */
38569     _setLoaded : function(){
38570         this.loaded = true;
38571     },
38572
38573     /** @private */
38574     closeClick : function(e){
38575         var o = {};
38576         e.stopEvent();
38577         this.fireEvent("beforeclose", this, o);
38578         if(o.cancel !== true){
38579             this.tabPanel.removeTab(this.id);
38580         }
38581     },
38582     /**
38583      * The text displayed in the tooltip for the close icon.
38584      * @type String
38585      */
38586     closeText : "Close this tab"
38587 });
38588 /**
38589 *    This script refer to:
38590 *    Title: International Telephone Input
38591 *    Author: Jack O'Connor
38592 *    Code version:  v12.1.12
38593 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38594 **/
38595
38596 Roo.bootstrap.PhoneInputData = function() {
38597     var d = [
38598       [
38599         "Afghanistan (‫افغانستان‬‎)",
38600         "af",
38601         "93"
38602       ],
38603       [
38604         "Albania (Shqipëri)",
38605         "al",
38606         "355"
38607       ],
38608       [
38609         "Algeria (‫الجزائر‬‎)",
38610         "dz",
38611         "213"
38612       ],
38613       [
38614         "American Samoa",
38615         "as",
38616         "1684"
38617       ],
38618       [
38619         "Andorra",
38620         "ad",
38621         "376"
38622       ],
38623       [
38624         "Angola",
38625         "ao",
38626         "244"
38627       ],
38628       [
38629         "Anguilla",
38630         "ai",
38631         "1264"
38632       ],
38633       [
38634         "Antigua and Barbuda",
38635         "ag",
38636         "1268"
38637       ],
38638       [
38639         "Argentina",
38640         "ar",
38641         "54"
38642       ],
38643       [
38644         "Armenia (Հայաստան)",
38645         "am",
38646         "374"
38647       ],
38648       [
38649         "Aruba",
38650         "aw",
38651         "297"
38652       ],
38653       [
38654         "Australia",
38655         "au",
38656         "61",
38657         0
38658       ],
38659       [
38660         "Austria (Österreich)",
38661         "at",
38662         "43"
38663       ],
38664       [
38665         "Azerbaijan (Azərbaycan)",
38666         "az",
38667         "994"
38668       ],
38669       [
38670         "Bahamas",
38671         "bs",
38672         "1242"
38673       ],
38674       [
38675         "Bahrain (‫البحرين‬‎)",
38676         "bh",
38677         "973"
38678       ],
38679       [
38680         "Bangladesh (বাংলাদেশ)",
38681         "bd",
38682         "880"
38683       ],
38684       [
38685         "Barbados",
38686         "bb",
38687         "1246"
38688       ],
38689       [
38690         "Belarus (Беларусь)",
38691         "by",
38692         "375"
38693       ],
38694       [
38695         "Belgium (België)",
38696         "be",
38697         "32"
38698       ],
38699       [
38700         "Belize",
38701         "bz",
38702         "501"
38703       ],
38704       [
38705         "Benin (Bénin)",
38706         "bj",
38707         "229"
38708       ],
38709       [
38710         "Bermuda",
38711         "bm",
38712         "1441"
38713       ],
38714       [
38715         "Bhutan (འབྲུག)",
38716         "bt",
38717         "975"
38718       ],
38719       [
38720         "Bolivia",
38721         "bo",
38722         "591"
38723       ],
38724       [
38725         "Bosnia and Herzegovina (Босна и Херцеговина)",
38726         "ba",
38727         "387"
38728       ],
38729       [
38730         "Botswana",
38731         "bw",
38732         "267"
38733       ],
38734       [
38735         "Brazil (Brasil)",
38736         "br",
38737         "55"
38738       ],
38739       [
38740         "British Indian Ocean Territory",
38741         "io",
38742         "246"
38743       ],
38744       [
38745         "British Virgin Islands",
38746         "vg",
38747         "1284"
38748       ],
38749       [
38750         "Brunei",
38751         "bn",
38752         "673"
38753       ],
38754       [
38755         "Bulgaria (България)",
38756         "bg",
38757         "359"
38758       ],
38759       [
38760         "Burkina Faso",
38761         "bf",
38762         "226"
38763       ],
38764       [
38765         "Burundi (Uburundi)",
38766         "bi",
38767         "257"
38768       ],
38769       [
38770         "Cambodia (កម្ពុជា)",
38771         "kh",
38772         "855"
38773       ],
38774       [
38775         "Cameroon (Cameroun)",
38776         "cm",
38777         "237"
38778       ],
38779       [
38780         "Canada",
38781         "ca",
38782         "1",
38783         1,
38784         ["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"]
38785       ],
38786       [
38787         "Cape Verde (Kabu Verdi)",
38788         "cv",
38789         "238"
38790       ],
38791       [
38792         "Caribbean Netherlands",
38793         "bq",
38794         "599",
38795         1
38796       ],
38797       [
38798         "Cayman Islands",
38799         "ky",
38800         "1345"
38801       ],
38802       [
38803         "Central African Republic (République centrafricaine)",
38804         "cf",
38805         "236"
38806       ],
38807       [
38808         "Chad (Tchad)",
38809         "td",
38810         "235"
38811       ],
38812       [
38813         "Chile",
38814         "cl",
38815         "56"
38816       ],
38817       [
38818         "China (中国)",
38819         "cn",
38820         "86"
38821       ],
38822       [
38823         "Christmas Island",
38824         "cx",
38825         "61",
38826         2
38827       ],
38828       [
38829         "Cocos (Keeling) Islands",
38830         "cc",
38831         "61",
38832         1
38833       ],
38834       [
38835         "Colombia",
38836         "co",
38837         "57"
38838       ],
38839       [
38840         "Comoros (‫جزر القمر‬‎)",
38841         "km",
38842         "269"
38843       ],
38844       [
38845         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38846         "cd",
38847         "243"
38848       ],
38849       [
38850         "Congo (Republic) (Congo-Brazzaville)",
38851         "cg",
38852         "242"
38853       ],
38854       [
38855         "Cook Islands",
38856         "ck",
38857         "682"
38858       ],
38859       [
38860         "Costa Rica",
38861         "cr",
38862         "506"
38863       ],
38864       [
38865         "Côte d’Ivoire",
38866         "ci",
38867         "225"
38868       ],
38869       [
38870         "Croatia (Hrvatska)",
38871         "hr",
38872         "385"
38873       ],
38874       [
38875         "Cuba",
38876         "cu",
38877         "53"
38878       ],
38879       [
38880         "Curaçao",
38881         "cw",
38882         "599",
38883         0
38884       ],
38885       [
38886         "Cyprus (Κύπρος)",
38887         "cy",
38888         "357"
38889       ],
38890       [
38891         "Czech Republic (Česká republika)",
38892         "cz",
38893         "420"
38894       ],
38895       [
38896         "Denmark (Danmark)",
38897         "dk",
38898         "45"
38899       ],
38900       [
38901         "Djibouti",
38902         "dj",
38903         "253"
38904       ],
38905       [
38906         "Dominica",
38907         "dm",
38908         "1767"
38909       ],
38910       [
38911         "Dominican Republic (República Dominicana)",
38912         "do",
38913         "1",
38914         2,
38915         ["809", "829", "849"]
38916       ],
38917       [
38918         "Ecuador",
38919         "ec",
38920         "593"
38921       ],
38922       [
38923         "Egypt (‫مصر‬‎)",
38924         "eg",
38925         "20"
38926       ],
38927       [
38928         "El Salvador",
38929         "sv",
38930         "503"
38931       ],
38932       [
38933         "Equatorial Guinea (Guinea Ecuatorial)",
38934         "gq",
38935         "240"
38936       ],
38937       [
38938         "Eritrea",
38939         "er",
38940         "291"
38941       ],
38942       [
38943         "Estonia (Eesti)",
38944         "ee",
38945         "372"
38946       ],
38947       [
38948         "Ethiopia",
38949         "et",
38950         "251"
38951       ],
38952       [
38953         "Falkland Islands (Islas Malvinas)",
38954         "fk",
38955         "500"
38956       ],
38957       [
38958         "Faroe Islands (Føroyar)",
38959         "fo",
38960         "298"
38961       ],
38962       [
38963         "Fiji",
38964         "fj",
38965         "679"
38966       ],
38967       [
38968         "Finland (Suomi)",
38969         "fi",
38970         "358",
38971         0
38972       ],
38973       [
38974         "France",
38975         "fr",
38976         "33"
38977       ],
38978       [
38979         "French Guiana (Guyane française)",
38980         "gf",
38981         "594"
38982       ],
38983       [
38984         "French Polynesia (Polynésie française)",
38985         "pf",
38986         "689"
38987       ],
38988       [
38989         "Gabon",
38990         "ga",
38991         "241"
38992       ],
38993       [
38994         "Gambia",
38995         "gm",
38996         "220"
38997       ],
38998       [
38999         "Georgia (საქართველო)",
39000         "ge",
39001         "995"
39002       ],
39003       [
39004         "Germany (Deutschland)",
39005         "de",
39006         "49"
39007       ],
39008       [
39009         "Ghana (Gaana)",
39010         "gh",
39011         "233"
39012       ],
39013       [
39014         "Gibraltar",
39015         "gi",
39016         "350"
39017       ],
39018       [
39019         "Greece (Ελλάδα)",
39020         "gr",
39021         "30"
39022       ],
39023       [
39024         "Greenland (Kalaallit Nunaat)",
39025         "gl",
39026         "299"
39027       ],
39028       [
39029         "Grenada",
39030         "gd",
39031         "1473"
39032       ],
39033       [
39034         "Guadeloupe",
39035         "gp",
39036         "590",
39037         0
39038       ],
39039       [
39040         "Guam",
39041         "gu",
39042         "1671"
39043       ],
39044       [
39045         "Guatemala",
39046         "gt",
39047         "502"
39048       ],
39049       [
39050         "Guernsey",
39051         "gg",
39052         "44",
39053         1
39054       ],
39055       [
39056         "Guinea (Guinée)",
39057         "gn",
39058         "224"
39059       ],
39060       [
39061         "Guinea-Bissau (Guiné Bissau)",
39062         "gw",
39063         "245"
39064       ],
39065       [
39066         "Guyana",
39067         "gy",
39068         "592"
39069       ],
39070       [
39071         "Haiti",
39072         "ht",
39073         "509"
39074       ],
39075       [
39076         "Honduras",
39077         "hn",
39078         "504"
39079       ],
39080       [
39081         "Hong Kong (香港)",
39082         "hk",
39083         "852"
39084       ],
39085       [
39086         "Hungary (Magyarország)",
39087         "hu",
39088         "36"
39089       ],
39090       [
39091         "Iceland (Ísland)",
39092         "is",
39093         "354"
39094       ],
39095       [
39096         "India (भारत)",
39097         "in",
39098         "91"
39099       ],
39100       [
39101         "Indonesia",
39102         "id",
39103         "62"
39104       ],
39105       [
39106         "Iran (‫ایران‬‎)",
39107         "ir",
39108         "98"
39109       ],
39110       [
39111         "Iraq (‫العراق‬‎)",
39112         "iq",
39113         "964"
39114       ],
39115       [
39116         "Ireland",
39117         "ie",
39118         "353"
39119       ],
39120       [
39121         "Isle of Man",
39122         "im",
39123         "44",
39124         2
39125       ],
39126       [
39127         "Israel (‫ישראל‬‎)",
39128         "il",
39129         "972"
39130       ],
39131       [
39132         "Italy (Italia)",
39133         "it",
39134         "39",
39135         0
39136       ],
39137       [
39138         "Jamaica",
39139         "jm",
39140         "1876"
39141       ],
39142       [
39143         "Japan (日本)",
39144         "jp",
39145         "81"
39146       ],
39147       [
39148         "Jersey",
39149         "je",
39150         "44",
39151         3
39152       ],
39153       [
39154         "Jordan (‫الأردن‬‎)",
39155         "jo",
39156         "962"
39157       ],
39158       [
39159         "Kazakhstan (Казахстан)",
39160         "kz",
39161         "7",
39162         1
39163       ],
39164       [
39165         "Kenya",
39166         "ke",
39167         "254"
39168       ],
39169       [
39170         "Kiribati",
39171         "ki",
39172         "686"
39173       ],
39174       [
39175         "Kosovo",
39176         "xk",
39177         "383"
39178       ],
39179       [
39180         "Kuwait (‫الكويت‬‎)",
39181         "kw",
39182         "965"
39183       ],
39184       [
39185         "Kyrgyzstan (Кыргызстан)",
39186         "kg",
39187         "996"
39188       ],
39189       [
39190         "Laos (ລາວ)",
39191         "la",
39192         "856"
39193       ],
39194       [
39195         "Latvia (Latvija)",
39196         "lv",
39197         "371"
39198       ],
39199       [
39200         "Lebanon (‫لبنان‬‎)",
39201         "lb",
39202         "961"
39203       ],
39204       [
39205         "Lesotho",
39206         "ls",
39207         "266"
39208       ],
39209       [
39210         "Liberia",
39211         "lr",
39212         "231"
39213       ],
39214       [
39215         "Libya (‫ليبيا‬‎)",
39216         "ly",
39217         "218"
39218       ],
39219       [
39220         "Liechtenstein",
39221         "li",
39222         "423"
39223       ],
39224       [
39225         "Lithuania (Lietuva)",
39226         "lt",
39227         "370"
39228       ],
39229       [
39230         "Luxembourg",
39231         "lu",
39232         "352"
39233       ],
39234       [
39235         "Macau (澳門)",
39236         "mo",
39237         "853"
39238       ],
39239       [
39240         "Macedonia (FYROM) (Македонија)",
39241         "mk",
39242         "389"
39243       ],
39244       [
39245         "Madagascar (Madagasikara)",
39246         "mg",
39247         "261"
39248       ],
39249       [
39250         "Malawi",
39251         "mw",
39252         "265"
39253       ],
39254       [
39255         "Malaysia",
39256         "my",
39257         "60"
39258       ],
39259       [
39260         "Maldives",
39261         "mv",
39262         "960"
39263       ],
39264       [
39265         "Mali",
39266         "ml",
39267         "223"
39268       ],
39269       [
39270         "Malta",
39271         "mt",
39272         "356"
39273       ],
39274       [
39275         "Marshall Islands",
39276         "mh",
39277         "692"
39278       ],
39279       [
39280         "Martinique",
39281         "mq",
39282         "596"
39283       ],
39284       [
39285         "Mauritania (‫موريتانيا‬‎)",
39286         "mr",
39287         "222"
39288       ],
39289       [
39290         "Mauritius (Moris)",
39291         "mu",
39292         "230"
39293       ],
39294       [
39295         "Mayotte",
39296         "yt",
39297         "262",
39298         1
39299       ],
39300       [
39301         "Mexico (México)",
39302         "mx",
39303         "52"
39304       ],
39305       [
39306         "Micronesia",
39307         "fm",
39308         "691"
39309       ],
39310       [
39311         "Moldova (Republica Moldova)",
39312         "md",
39313         "373"
39314       ],
39315       [
39316         "Monaco",
39317         "mc",
39318         "377"
39319       ],
39320       [
39321         "Mongolia (Монгол)",
39322         "mn",
39323         "976"
39324       ],
39325       [
39326         "Montenegro (Crna Gora)",
39327         "me",
39328         "382"
39329       ],
39330       [
39331         "Montserrat",
39332         "ms",
39333         "1664"
39334       ],
39335       [
39336         "Morocco (‫المغرب‬‎)",
39337         "ma",
39338         "212",
39339         0
39340       ],
39341       [
39342         "Mozambique (Moçambique)",
39343         "mz",
39344         "258"
39345       ],
39346       [
39347         "Myanmar (Burma) (မြန်မာ)",
39348         "mm",
39349         "95"
39350       ],
39351       [
39352         "Namibia (Namibië)",
39353         "na",
39354         "264"
39355       ],
39356       [
39357         "Nauru",
39358         "nr",
39359         "674"
39360       ],
39361       [
39362         "Nepal (नेपाल)",
39363         "np",
39364         "977"
39365       ],
39366       [
39367         "Netherlands (Nederland)",
39368         "nl",
39369         "31"
39370       ],
39371       [
39372         "New Caledonia (Nouvelle-Calédonie)",
39373         "nc",
39374         "687"
39375       ],
39376       [
39377         "New Zealand",
39378         "nz",
39379         "64"
39380       ],
39381       [
39382         "Nicaragua",
39383         "ni",
39384         "505"
39385       ],
39386       [
39387         "Niger (Nijar)",
39388         "ne",
39389         "227"
39390       ],
39391       [
39392         "Nigeria",
39393         "ng",
39394         "234"
39395       ],
39396       [
39397         "Niue",
39398         "nu",
39399         "683"
39400       ],
39401       [
39402         "Norfolk Island",
39403         "nf",
39404         "672"
39405       ],
39406       [
39407         "North Korea (조선 민주주의 인민 공화국)",
39408         "kp",
39409         "850"
39410       ],
39411       [
39412         "Northern Mariana Islands",
39413         "mp",
39414         "1670"
39415       ],
39416       [
39417         "Norway (Norge)",
39418         "no",
39419         "47",
39420         0
39421       ],
39422       [
39423         "Oman (‫عُمان‬‎)",
39424         "om",
39425         "968"
39426       ],
39427       [
39428         "Pakistan (‫پاکستان‬‎)",
39429         "pk",
39430         "92"
39431       ],
39432       [
39433         "Palau",
39434         "pw",
39435         "680"
39436       ],
39437       [
39438         "Palestine (‫فلسطين‬‎)",
39439         "ps",
39440         "970"
39441       ],
39442       [
39443         "Panama (Panamá)",
39444         "pa",
39445         "507"
39446       ],
39447       [
39448         "Papua New Guinea",
39449         "pg",
39450         "675"
39451       ],
39452       [
39453         "Paraguay",
39454         "py",
39455         "595"
39456       ],
39457       [
39458         "Peru (Perú)",
39459         "pe",
39460         "51"
39461       ],
39462       [
39463         "Philippines",
39464         "ph",
39465         "63"
39466       ],
39467       [
39468         "Poland (Polska)",
39469         "pl",
39470         "48"
39471       ],
39472       [
39473         "Portugal",
39474         "pt",
39475         "351"
39476       ],
39477       [
39478         "Puerto Rico",
39479         "pr",
39480         "1",
39481         3,
39482         ["787", "939"]
39483       ],
39484       [
39485         "Qatar (‫قطر‬‎)",
39486         "qa",
39487         "974"
39488       ],
39489       [
39490         "Réunion (La Réunion)",
39491         "re",
39492         "262",
39493         0
39494       ],
39495       [
39496         "Romania (România)",
39497         "ro",
39498         "40"
39499       ],
39500       [
39501         "Russia (Россия)",
39502         "ru",
39503         "7",
39504         0
39505       ],
39506       [
39507         "Rwanda",
39508         "rw",
39509         "250"
39510       ],
39511       [
39512         "Saint Barthélemy",
39513         "bl",
39514         "590",
39515         1
39516       ],
39517       [
39518         "Saint Helena",
39519         "sh",
39520         "290"
39521       ],
39522       [
39523         "Saint Kitts and Nevis",
39524         "kn",
39525         "1869"
39526       ],
39527       [
39528         "Saint Lucia",
39529         "lc",
39530         "1758"
39531       ],
39532       [
39533         "Saint Martin (Saint-Martin (partie française))",
39534         "mf",
39535         "590",
39536         2
39537       ],
39538       [
39539         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39540         "pm",
39541         "508"
39542       ],
39543       [
39544         "Saint Vincent and the Grenadines",
39545         "vc",
39546         "1784"
39547       ],
39548       [
39549         "Samoa",
39550         "ws",
39551         "685"
39552       ],
39553       [
39554         "San Marino",
39555         "sm",
39556         "378"
39557       ],
39558       [
39559         "São Tomé and Príncipe (São Tomé e Príncipe)",
39560         "st",
39561         "239"
39562       ],
39563       [
39564         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39565         "sa",
39566         "966"
39567       ],
39568       [
39569         "Senegal (Sénégal)",
39570         "sn",
39571         "221"
39572       ],
39573       [
39574         "Serbia (Србија)",
39575         "rs",
39576         "381"
39577       ],
39578       [
39579         "Seychelles",
39580         "sc",
39581         "248"
39582       ],
39583       [
39584         "Sierra Leone",
39585         "sl",
39586         "232"
39587       ],
39588       [
39589         "Singapore",
39590         "sg",
39591         "65"
39592       ],
39593       [
39594         "Sint Maarten",
39595         "sx",
39596         "1721"
39597       ],
39598       [
39599         "Slovakia (Slovensko)",
39600         "sk",
39601         "421"
39602       ],
39603       [
39604         "Slovenia (Slovenija)",
39605         "si",
39606         "386"
39607       ],
39608       [
39609         "Solomon Islands",
39610         "sb",
39611         "677"
39612       ],
39613       [
39614         "Somalia (Soomaaliya)",
39615         "so",
39616         "252"
39617       ],
39618       [
39619         "South Africa",
39620         "za",
39621         "27"
39622       ],
39623       [
39624         "South Korea (대한민국)",
39625         "kr",
39626         "82"
39627       ],
39628       [
39629         "South Sudan (‫جنوب السودان‬‎)",
39630         "ss",
39631         "211"
39632       ],
39633       [
39634         "Spain (España)",
39635         "es",
39636         "34"
39637       ],
39638       [
39639         "Sri Lanka (ශ්‍රී ලංකාව)",
39640         "lk",
39641         "94"
39642       ],
39643       [
39644         "Sudan (‫السودان‬‎)",
39645         "sd",
39646         "249"
39647       ],
39648       [
39649         "Suriname",
39650         "sr",
39651         "597"
39652       ],
39653       [
39654         "Svalbard and Jan Mayen",
39655         "sj",
39656         "47",
39657         1
39658       ],
39659       [
39660         "Swaziland",
39661         "sz",
39662         "268"
39663       ],
39664       [
39665         "Sweden (Sverige)",
39666         "se",
39667         "46"
39668       ],
39669       [
39670         "Switzerland (Schweiz)",
39671         "ch",
39672         "41"
39673       ],
39674       [
39675         "Syria (‫سوريا‬‎)",
39676         "sy",
39677         "963"
39678       ],
39679       [
39680         "Taiwan (台灣)",
39681         "tw",
39682         "886"
39683       ],
39684       [
39685         "Tajikistan",
39686         "tj",
39687         "992"
39688       ],
39689       [
39690         "Tanzania",
39691         "tz",
39692         "255"
39693       ],
39694       [
39695         "Thailand (ไทย)",
39696         "th",
39697         "66"
39698       ],
39699       [
39700         "Timor-Leste",
39701         "tl",
39702         "670"
39703       ],
39704       [
39705         "Togo",
39706         "tg",
39707         "228"
39708       ],
39709       [
39710         "Tokelau",
39711         "tk",
39712         "690"
39713       ],
39714       [
39715         "Tonga",
39716         "to",
39717         "676"
39718       ],
39719       [
39720         "Trinidad and Tobago",
39721         "tt",
39722         "1868"
39723       ],
39724       [
39725         "Tunisia (‫تونس‬‎)",
39726         "tn",
39727         "216"
39728       ],
39729       [
39730         "Turkey (Türkiye)",
39731         "tr",
39732         "90"
39733       ],
39734       [
39735         "Turkmenistan",
39736         "tm",
39737         "993"
39738       ],
39739       [
39740         "Turks and Caicos Islands",
39741         "tc",
39742         "1649"
39743       ],
39744       [
39745         "Tuvalu",
39746         "tv",
39747         "688"
39748       ],
39749       [
39750         "U.S. Virgin Islands",
39751         "vi",
39752         "1340"
39753       ],
39754       [
39755         "Uganda",
39756         "ug",
39757         "256"
39758       ],
39759       [
39760         "Ukraine (Україна)",
39761         "ua",
39762         "380"
39763       ],
39764       [
39765         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39766         "ae",
39767         "971"
39768       ],
39769       [
39770         "United Kingdom",
39771         "gb",
39772         "44",
39773         0
39774       ],
39775       [
39776         "United States",
39777         "us",
39778         "1",
39779         0
39780       ],
39781       [
39782         "Uruguay",
39783         "uy",
39784         "598"
39785       ],
39786       [
39787         "Uzbekistan (Oʻzbekiston)",
39788         "uz",
39789         "998"
39790       ],
39791       [
39792         "Vanuatu",
39793         "vu",
39794         "678"
39795       ],
39796       [
39797         "Vatican City (Città del Vaticano)",
39798         "va",
39799         "39",
39800         1
39801       ],
39802       [
39803         "Venezuela",
39804         "ve",
39805         "58"
39806       ],
39807       [
39808         "Vietnam (Việt Nam)",
39809         "vn",
39810         "84"
39811       ],
39812       [
39813         "Wallis and Futuna (Wallis-et-Futuna)",
39814         "wf",
39815         "681"
39816       ],
39817       [
39818         "Western Sahara (‫الصحراء الغربية‬‎)",
39819         "eh",
39820         "212",
39821         1
39822       ],
39823       [
39824         "Yemen (‫اليمن‬‎)",
39825         "ye",
39826         "967"
39827       ],
39828       [
39829         "Zambia",
39830         "zm",
39831         "260"
39832       ],
39833       [
39834         "Zimbabwe",
39835         "zw",
39836         "263"
39837       ],
39838       [
39839         "Åland Islands",
39840         "ax",
39841         "358",
39842         1
39843       ]
39844   ];
39845   
39846   return d;
39847 }/**
39848 *    This script refer to:
39849 *    Title: International Telephone Input
39850 *    Author: Jack O'Connor
39851 *    Code version:  v12.1.12
39852 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39853 **/
39854
39855 /**
39856  * @class Roo.bootstrap.PhoneInput
39857  * @extends Roo.bootstrap.TriggerField
39858  * An input with International dial-code selection
39859  
39860  * @cfg {String} defaultDialCode default '+852'
39861  * @cfg {Array} preferedCountries default []
39862   
39863  * @constructor
39864  * Create a new PhoneInput.
39865  * @param {Object} config Configuration options
39866  */
39867
39868 Roo.bootstrap.PhoneInput = function(config) {
39869     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39870 };
39871
39872 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39873         
39874         listWidth: undefined,
39875         
39876         selectedClass: 'active',
39877         
39878         invalidClass : "has-warning",
39879         
39880         validClass: 'has-success',
39881         
39882         allowed: '0123456789',
39883         
39884         max_length: 15,
39885         
39886         /**
39887          * @cfg {String} defaultDialCode The default dial code when initializing the input
39888          */
39889         defaultDialCode: '+852',
39890         
39891         /**
39892          * @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
39893          */
39894         preferedCountries: false,
39895         
39896         getAutoCreate : function()
39897         {
39898             var data = Roo.bootstrap.PhoneInputData();
39899             var align = this.labelAlign || this.parentLabelAlign();
39900             var id = Roo.id();
39901             
39902             this.allCountries = [];
39903             this.dialCodeMapping = [];
39904             
39905             for (var i = 0; i < data.length; i++) {
39906               var c = data[i];
39907               this.allCountries[i] = {
39908                 name: c[0],
39909                 iso2: c[1],
39910                 dialCode: c[2],
39911                 priority: c[3] || 0,
39912                 areaCodes: c[4] || null
39913               };
39914               this.dialCodeMapping[c[2]] = {
39915                   name: c[0],
39916                   iso2: c[1],
39917                   priority: c[3] || 0,
39918                   areaCodes: c[4] || null
39919               };
39920             }
39921             
39922             var cfg = {
39923                 cls: 'form-group',
39924                 cn: []
39925             };
39926             
39927             var input =  {
39928                 tag: 'input',
39929                 id : id,
39930                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39931                 maxlength: this.max_length,
39932                 cls : 'form-control tel-input',
39933                 autocomplete: 'new-password'
39934             };
39935             
39936             var hiddenInput = {
39937                 tag: 'input',
39938                 type: 'hidden',
39939                 cls: 'hidden-tel-input'
39940             };
39941             
39942             if (this.name) {
39943                 hiddenInput.name = this.name;
39944             }
39945             
39946             if (this.disabled) {
39947                 input.disabled = true;
39948             }
39949             
39950             var flag_container = {
39951                 tag: 'div',
39952                 cls: 'flag-box',
39953                 cn: [
39954                     {
39955                         tag: 'div',
39956                         cls: 'flag'
39957                     },
39958                     {
39959                         tag: 'div',
39960                         cls: 'caret'
39961                     }
39962                 ]
39963             };
39964             
39965             var box = {
39966                 tag: 'div',
39967                 cls: this.hasFeedback ? 'has-feedback' : '',
39968                 cn: [
39969                     hiddenInput,
39970                     input,
39971                     {
39972                         tag: 'input',
39973                         cls: 'dial-code-holder',
39974                         disabled: true
39975                     }
39976                 ]
39977             };
39978             
39979             var container = {
39980                 cls: 'roo-select2-container input-group',
39981                 cn: [
39982                     flag_container,
39983                     box
39984                 ]
39985             };
39986             
39987             if (this.fieldLabel.length) {
39988                 var indicator = {
39989                     tag: 'i',
39990                     tooltip: 'This field is required'
39991                 };
39992                 
39993                 var label = {
39994                     tag: 'label',
39995                     'for':  id,
39996                     cls: 'control-label',
39997                     cn: []
39998                 };
39999                 
40000                 var label_text = {
40001                     tag: 'span',
40002                     html: this.fieldLabel
40003                 };
40004                 
40005                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40006                 label.cn = [
40007                     indicator,
40008                     label_text
40009                 ];
40010                 
40011                 if(this.indicatorpos == 'right') {
40012                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40013                     label.cn = [
40014                         label_text,
40015                         indicator
40016                     ];
40017                 }
40018                 
40019                 if(align == 'left') {
40020                     container = {
40021                         tag: 'div',
40022                         cn: [
40023                             container
40024                         ]
40025                     };
40026                     
40027                     if(this.labelWidth > 12){
40028                         label.style = "width: " + this.labelWidth + 'px';
40029                     }
40030                     if(this.labelWidth < 13 && this.labelmd == 0){
40031                         this.labelmd = this.labelWidth;
40032                     }
40033                     if(this.labellg > 0){
40034                         label.cls += ' col-lg-' + this.labellg;
40035                         input.cls += ' col-lg-' + (12 - this.labellg);
40036                     }
40037                     if(this.labelmd > 0){
40038                         label.cls += ' col-md-' + this.labelmd;
40039                         container.cls += ' col-md-' + (12 - this.labelmd);
40040                     }
40041                     if(this.labelsm > 0){
40042                         label.cls += ' col-sm-' + this.labelsm;
40043                         container.cls += ' col-sm-' + (12 - this.labelsm);
40044                     }
40045                     if(this.labelxs > 0){
40046                         label.cls += ' col-xs-' + this.labelxs;
40047                         container.cls += ' col-xs-' + (12 - this.labelxs);
40048                     }
40049                 }
40050             }
40051             
40052             cfg.cn = [
40053                 label,
40054                 container
40055             ];
40056             
40057             var settings = this;
40058             
40059             ['xs','sm','md','lg'].map(function(size){
40060                 if (settings[size]) {
40061                     cfg.cls += ' col-' + size + '-' + settings[size];
40062                 }
40063             });
40064             
40065             this.store = new Roo.data.Store({
40066                 proxy : new Roo.data.MemoryProxy({}),
40067                 reader : new Roo.data.JsonReader({
40068                     fields : [
40069                         {
40070                             'name' : 'name',
40071                             'type' : 'string'
40072                         },
40073                         {
40074                             'name' : 'iso2',
40075                             'type' : 'string'
40076                         },
40077                         {
40078                             'name' : 'dialCode',
40079                             'type' : 'string'
40080                         },
40081                         {
40082                             'name' : 'priority',
40083                             'type' : 'string'
40084                         },
40085                         {
40086                             'name' : 'areaCodes',
40087                             'type' : 'string'
40088                         }
40089                     ]
40090                 })
40091             });
40092             
40093             if(!this.preferedCountries) {
40094                 this.preferedCountries = [
40095                     'hk',
40096                     'gb',
40097                     'us'
40098                 ];
40099             }
40100             
40101             var p = this.preferedCountries.reverse();
40102             
40103             if(p) {
40104                 for (var i = 0; i < p.length; i++) {
40105                     for (var j = 0; j < this.allCountries.length; j++) {
40106                         if(this.allCountries[j].iso2 == p[i]) {
40107                             var t = this.allCountries[j];
40108                             this.allCountries.splice(j,1);
40109                             this.allCountries.unshift(t);
40110                         }
40111                     } 
40112                 }
40113             }
40114             
40115             this.store.proxy.data = {
40116                 success: true,
40117                 data: this.allCountries
40118             };
40119             
40120             return cfg;
40121         },
40122         
40123         initEvents : function()
40124         {
40125             this.createList();
40126             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40127             
40128             this.indicator = this.indicatorEl();
40129             this.flag = this.flagEl();
40130             this.dialCodeHolder = this.dialCodeHolderEl();
40131             
40132             this.trigger = this.el.select('div.flag-box',true).first();
40133             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40134             
40135             var _this = this;
40136             
40137             (function(){
40138                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40139                 _this.list.setWidth(lw);
40140             }).defer(100);
40141             
40142             this.list.on('mouseover', this.onViewOver, this);
40143             this.list.on('mousemove', this.onViewMove, this);
40144             this.inputEl().on("keyup", this.onKeyUp, this);
40145             this.inputEl().on("keypress", this.onKeyPress, this);
40146             
40147             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40148
40149             this.view = new Roo.View(this.list, this.tpl, {
40150                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40151             });
40152             
40153             this.view.on('click', this.onViewClick, this);
40154             this.setValue(this.defaultDialCode);
40155         },
40156         
40157         onTriggerClick : function(e)
40158         {
40159             Roo.log('trigger click');
40160             if(this.disabled){
40161                 return;
40162             }
40163             
40164             if(this.isExpanded()){
40165                 this.collapse();
40166                 this.hasFocus = false;
40167             }else {
40168                 this.store.load({});
40169                 this.hasFocus = true;
40170                 this.expand();
40171             }
40172         },
40173         
40174         isExpanded : function()
40175         {
40176             return this.list.isVisible();
40177         },
40178         
40179         collapse : function()
40180         {
40181             if(!this.isExpanded()){
40182                 return;
40183             }
40184             this.list.hide();
40185             Roo.get(document).un('mousedown', this.collapseIf, this);
40186             Roo.get(document).un('mousewheel', this.collapseIf, this);
40187             this.fireEvent('collapse', this);
40188             this.validate();
40189         },
40190         
40191         expand : function()
40192         {
40193             Roo.log('expand');
40194
40195             if(this.isExpanded() || !this.hasFocus){
40196                 return;
40197             }
40198             
40199             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40200             this.list.setWidth(lw);
40201             
40202             this.list.show();
40203             this.restrictHeight();
40204             
40205             Roo.get(document).on('mousedown', this.collapseIf, this);
40206             Roo.get(document).on('mousewheel', this.collapseIf, this);
40207             
40208             this.fireEvent('expand', this);
40209         },
40210         
40211         restrictHeight : function()
40212         {
40213             this.list.alignTo(this.inputEl(), this.listAlign);
40214             this.list.alignTo(this.inputEl(), this.listAlign);
40215         },
40216         
40217         onViewOver : function(e, t)
40218         {
40219             if(this.inKeyMode){
40220                 return;
40221             }
40222             var item = this.view.findItemFromChild(t);
40223             
40224             if(item){
40225                 var index = this.view.indexOf(item);
40226                 this.select(index, false);
40227             }
40228         },
40229
40230         // private
40231         onViewClick : function(view, doFocus, el, e)
40232         {
40233             var index = this.view.getSelectedIndexes()[0];
40234             
40235             var r = this.store.getAt(index);
40236             
40237             if(r){
40238                 this.onSelect(r, index);
40239             }
40240             if(doFocus !== false && !this.blockFocus){
40241                 this.inputEl().focus();
40242             }
40243         },
40244         
40245         onViewMove : function(e, t)
40246         {
40247             this.inKeyMode = false;
40248         },
40249         
40250         select : function(index, scrollIntoView)
40251         {
40252             this.selectedIndex = index;
40253             this.view.select(index);
40254             if(scrollIntoView !== false){
40255                 var el = this.view.getNode(index);
40256                 if(el){
40257                     this.list.scrollChildIntoView(el, false);
40258                 }
40259             }
40260         },
40261         
40262         createList : function()
40263         {
40264             this.list = Roo.get(document.body).createChild({
40265                 tag: 'ul',
40266                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40267                 style: 'display:none'
40268             });
40269             
40270             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40271         },
40272         
40273         collapseIf : function(e)
40274         {
40275             var in_combo  = e.within(this.el);
40276             var in_list =  e.within(this.list);
40277             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40278             
40279             if (in_combo || in_list || is_list) {
40280                 return;
40281             }
40282             this.collapse();
40283         },
40284         
40285         onSelect : function(record, index)
40286         {
40287             if(this.fireEvent('beforeselect', this, record, index) !== false){
40288                 
40289                 this.setFlagClass(record.data.iso2);
40290                 this.setDialCode(record.data.dialCode);
40291                 this.hasFocus = false;
40292                 this.collapse();
40293                 this.fireEvent('select', this, record, index);
40294             }
40295         },
40296         
40297         flagEl : function()
40298         {
40299             var flag = this.el.select('div.flag',true).first();
40300             if(!flag){
40301                 return false;
40302             }
40303             return flag;
40304         },
40305         
40306         dialCodeHolderEl : function()
40307         {
40308             var d = this.el.select('input.dial-code-holder',true).first();
40309             if(!d){
40310                 return false;
40311             }
40312             return d;
40313         },
40314         
40315         setDialCode : function(v)
40316         {
40317             this.dialCodeHolder.dom.value = '+'+v;
40318         },
40319         
40320         setFlagClass : function(n)
40321         {
40322             this.flag.dom.className = 'flag '+n;
40323         },
40324         
40325         getValue : function()
40326         {
40327             var v = this.inputEl().getValue();
40328             if(this.dialCodeHolder) {
40329                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40330             }
40331             return v;
40332         },
40333         
40334         setValue : function(v)
40335         {
40336             var d = this.getDialCode(v);
40337             
40338             //invalid dial code
40339             if(v.length == 0 || !d || d.length == 0) {
40340                 if(this.rendered){
40341                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40342                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40343                 }
40344                 return;
40345             }
40346             
40347             //valid dial code
40348             this.setFlagClass(this.dialCodeMapping[d].iso2);
40349             this.setDialCode(d);
40350             this.inputEl().dom.value = v.replace('+'+d,'');
40351             this.hiddenEl().dom.value = this.getValue();
40352             
40353             this.validate();
40354         },
40355         
40356         getDialCode : function(v)
40357         {
40358             v = v ||  '';
40359             
40360             if (v.length == 0) {
40361                 return this.dialCodeHolder.dom.value;
40362             }
40363             
40364             var dialCode = "";
40365             if (v.charAt(0) != "+") {
40366                 return false;
40367             }
40368             var numericChars = "";
40369             for (var i = 1; i < v.length; i++) {
40370               var c = v.charAt(i);
40371               if (!isNaN(c)) {
40372                 numericChars += c;
40373                 if (this.dialCodeMapping[numericChars]) {
40374                   dialCode = v.substr(1, i);
40375                 }
40376                 if (numericChars.length == 4) {
40377                   break;
40378                 }
40379               }
40380             }
40381             return dialCode;
40382         },
40383         
40384         reset : function()
40385         {
40386             this.setValue(this.defaultDialCode);
40387             this.validate();
40388         },
40389         
40390         hiddenEl : function()
40391         {
40392             return this.el.select('input.hidden-tel-input',true).first();
40393         },
40394         
40395         // after setting val
40396         onKeyUp : function(e){
40397             this.setValue(this.getValue());
40398         },
40399         
40400         onKeyPress : function(e){
40401             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40402                 e.stopEvent();
40403             }
40404         }
40405         
40406 });
40407 /**
40408  * @class Roo.bootstrap.MoneyField
40409  * @extends Roo.bootstrap.ComboBox
40410  * Bootstrap MoneyField class
40411  * 
40412  * @constructor
40413  * Create a new MoneyField.
40414  * @param {Object} config Configuration options
40415  */
40416
40417 Roo.bootstrap.MoneyField = function(config) {
40418     
40419     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40420     
40421 };
40422
40423 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40424     
40425     /**
40426      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40427      */
40428     allowDecimals : true,
40429     /**
40430      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40431      */
40432     decimalSeparator : ".",
40433     /**
40434      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40435      */
40436     decimalPrecision : 0,
40437     /**
40438      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40439      */
40440     allowNegative : true,
40441     /**
40442      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40443      */
40444     allowZero: true,
40445     /**
40446      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40447      */
40448     minValue : Number.NEGATIVE_INFINITY,
40449     /**
40450      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40451      */
40452     maxValue : Number.MAX_VALUE,
40453     /**
40454      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40455      */
40456     minText : "The minimum value for this field is {0}",
40457     /**
40458      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40459      */
40460     maxText : "The maximum value for this field is {0}",
40461     /**
40462      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40463      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40464      */
40465     nanText : "{0} is not a valid number",
40466     /**
40467      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40468      */
40469     castInt : true,
40470     /**
40471      * @cfg {String} defaults currency of the MoneyField
40472      * value should be in lkey
40473      */
40474     defaultCurrency : false,
40475     /**
40476      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40477      */
40478     thousandsDelimiter : false,
40479     /**
40480      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40481      */
40482     max_length: false,
40483     
40484     inputlg : 9,
40485     inputmd : 9,
40486     inputsm : 9,
40487     inputxs : 6,
40488     
40489     store : false,
40490     
40491     getAutoCreate : function()
40492     {
40493         var align = this.labelAlign || this.parentLabelAlign();
40494         
40495         var id = Roo.id();
40496
40497         var cfg = {
40498             cls: 'form-group',
40499             cn: []
40500         };
40501
40502         var input =  {
40503             tag: 'input',
40504             id : id,
40505             cls : 'form-control roo-money-amount-input',
40506             autocomplete: 'new-password'
40507         };
40508         
40509         var hiddenInput = {
40510             tag: 'input',
40511             type: 'hidden',
40512             id: Roo.id(),
40513             cls: 'hidden-number-input'
40514         };
40515         
40516         if(this.max_length) {
40517             input.maxlength = this.max_length; 
40518         }
40519         
40520         if (this.name) {
40521             hiddenInput.name = this.name;
40522         }
40523
40524         if (this.disabled) {
40525             input.disabled = true;
40526         }
40527
40528         var clg = 12 - this.inputlg;
40529         var cmd = 12 - this.inputmd;
40530         var csm = 12 - this.inputsm;
40531         var cxs = 12 - this.inputxs;
40532         
40533         var container = {
40534             tag : 'div',
40535             cls : 'row roo-money-field',
40536             cn : [
40537                 {
40538                     tag : 'div',
40539                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40540                     cn : [
40541                         {
40542                             tag : 'div',
40543                             cls: 'roo-select2-container input-group',
40544                             cn: [
40545                                 {
40546                                     tag : 'input',
40547                                     cls : 'form-control roo-money-currency-input',
40548                                     autocomplete: 'new-password',
40549                                     readOnly : 1,
40550                                     name : this.currencyName
40551                                 },
40552                                 {
40553                                     tag :'span',
40554                                     cls : 'input-group-addon',
40555                                     cn : [
40556                                         {
40557                                             tag: 'span',
40558                                             cls: 'caret'
40559                                         }
40560                                     ]
40561                                 }
40562                             ]
40563                         }
40564                     ]
40565                 },
40566                 {
40567                     tag : 'div',
40568                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40569                     cn : [
40570                         {
40571                             tag: 'div',
40572                             cls: this.hasFeedback ? 'has-feedback' : '',
40573                             cn: [
40574                                 input
40575                             ]
40576                         }
40577                     ]
40578                 }
40579             ]
40580             
40581         };
40582         
40583         if (this.fieldLabel.length) {
40584             var indicator = {
40585                 tag: 'i',
40586                 tooltip: 'This field is required'
40587             };
40588
40589             var label = {
40590                 tag: 'label',
40591                 'for':  id,
40592                 cls: 'control-label',
40593                 cn: []
40594             };
40595
40596             var label_text = {
40597                 tag: 'span',
40598                 html: this.fieldLabel
40599             };
40600
40601             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40602             label.cn = [
40603                 indicator,
40604                 label_text
40605             ];
40606
40607             if(this.indicatorpos == 'right') {
40608                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40609                 label.cn = [
40610                     label_text,
40611                     indicator
40612                 ];
40613             }
40614
40615             if(align == 'left') {
40616                 container = {
40617                     tag: 'div',
40618                     cn: [
40619                         container
40620                     ]
40621                 };
40622
40623                 if(this.labelWidth > 12){
40624                     label.style = "width: " + this.labelWidth + 'px';
40625                 }
40626                 if(this.labelWidth < 13 && this.labelmd == 0){
40627                     this.labelmd = this.labelWidth;
40628                 }
40629                 if(this.labellg > 0){
40630                     label.cls += ' col-lg-' + this.labellg;
40631                     input.cls += ' col-lg-' + (12 - this.labellg);
40632                 }
40633                 if(this.labelmd > 0){
40634                     label.cls += ' col-md-' + this.labelmd;
40635                     container.cls += ' col-md-' + (12 - this.labelmd);
40636                 }
40637                 if(this.labelsm > 0){
40638                     label.cls += ' col-sm-' + this.labelsm;
40639                     container.cls += ' col-sm-' + (12 - this.labelsm);
40640                 }
40641                 if(this.labelxs > 0){
40642                     label.cls += ' col-xs-' + this.labelxs;
40643                     container.cls += ' col-xs-' + (12 - this.labelxs);
40644                 }
40645             }
40646         }
40647
40648         cfg.cn = [
40649             label,
40650             container,
40651             hiddenInput
40652         ];
40653         
40654         var settings = this;
40655
40656         ['xs','sm','md','lg'].map(function(size){
40657             if (settings[size]) {
40658                 cfg.cls += ' col-' + size + '-' + settings[size];
40659             }
40660         });
40661         
40662         return cfg;
40663     },
40664     
40665     initEvents : function()
40666     {
40667         this.indicator = this.indicatorEl();
40668         
40669         this.initCurrencyEvent();
40670         
40671         this.initNumberEvent();
40672     },
40673     
40674     initCurrencyEvent : function()
40675     {
40676         if (!this.store) {
40677             throw "can not find store for combo";
40678         }
40679         
40680         this.store = Roo.factory(this.store, Roo.data);
40681         this.store.parent = this;
40682         
40683         this.createList();
40684         
40685         this.triggerEl = this.el.select('.input-group-addon', true).first();
40686         
40687         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40688         
40689         var _this = this;
40690         
40691         (function(){
40692             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40693             _this.list.setWidth(lw);
40694         }).defer(100);
40695         
40696         this.list.on('mouseover', this.onViewOver, this);
40697         this.list.on('mousemove', this.onViewMove, this);
40698         this.list.on('scroll', this.onViewScroll, this);
40699         
40700         if(!this.tpl){
40701             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40702         }
40703         
40704         this.view = new Roo.View(this.list, this.tpl, {
40705             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40706         });
40707         
40708         this.view.on('click', this.onViewClick, this);
40709         
40710         this.store.on('beforeload', this.onBeforeLoad, this);
40711         this.store.on('load', this.onLoad, this);
40712         this.store.on('loadexception', this.onLoadException, this);
40713         
40714         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40715             "up" : function(e){
40716                 this.inKeyMode = true;
40717                 this.selectPrev();
40718             },
40719
40720             "down" : function(e){
40721                 if(!this.isExpanded()){
40722                     this.onTriggerClick();
40723                 }else{
40724                     this.inKeyMode = true;
40725                     this.selectNext();
40726                 }
40727             },
40728
40729             "enter" : function(e){
40730                 this.collapse();
40731                 
40732                 if(this.fireEvent("specialkey", this, e)){
40733                     this.onViewClick(false);
40734                 }
40735                 
40736                 return true;
40737             },
40738
40739             "esc" : function(e){
40740                 this.collapse();
40741             },
40742
40743             "tab" : 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             scope : this,
40754
40755             doRelay : function(foo, bar, hname){
40756                 if(hname == 'down' || this.scope.isExpanded()){
40757                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40758                 }
40759                 return true;
40760             },
40761
40762             forceKeyDown: true
40763         });
40764         
40765         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40766         
40767     },
40768     
40769     initNumberEvent : function(e)
40770     {
40771         this.inputEl().on("keydown" , this.fireKey,  this);
40772         this.inputEl().on("focus", this.onFocus,  this);
40773         this.inputEl().on("blur", this.onBlur,  this);
40774         
40775         this.inputEl().relayEvent('keyup', this);
40776         
40777         if(this.indicator){
40778             this.indicator.addClass('invisible');
40779         }
40780  
40781         this.originalValue = this.getValue();
40782         
40783         if(this.validationEvent == 'keyup'){
40784             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40785             this.inputEl().on('keyup', this.filterValidation, this);
40786         }
40787         else if(this.validationEvent !== false){
40788             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40789         }
40790         
40791         if(this.selectOnFocus){
40792             this.on("focus", this.preFocus, this);
40793             
40794         }
40795         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40796             this.inputEl().on("keypress", this.filterKeys, this);
40797         } else {
40798             this.inputEl().relayEvent('keypress', this);
40799         }
40800         
40801         var allowed = "0123456789";
40802         
40803         if(this.allowDecimals){
40804             allowed += this.decimalSeparator;
40805         }
40806         
40807         if(this.allowNegative){
40808             allowed += "-";
40809         }
40810         
40811         if(this.thousandsDelimiter) {
40812             allowed += ",";
40813         }
40814         
40815         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40816         
40817         var keyPress = function(e){
40818             
40819             var k = e.getKey();
40820             
40821             var c = e.getCharCode();
40822             
40823             if(
40824                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40825                     allowed.indexOf(String.fromCharCode(c)) === -1
40826             ){
40827                 e.stopEvent();
40828                 return;
40829             }
40830             
40831             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40832                 return;
40833             }
40834             
40835             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40836                 e.stopEvent();
40837             }
40838         };
40839         
40840         this.inputEl().on("keypress", keyPress, this);
40841         
40842     },
40843     
40844     onTriggerClick : function(e)
40845     {   
40846         if(this.disabled){
40847             return;
40848         }
40849         
40850         this.page = 0;
40851         this.loadNext = false;
40852         
40853         if(this.isExpanded()){
40854             this.collapse();
40855             return;
40856         }
40857         
40858         this.hasFocus = true;
40859         
40860         if(this.triggerAction == 'all') {
40861             this.doQuery(this.allQuery, true);
40862             return;
40863         }
40864         
40865         this.doQuery(this.getRawValue());
40866     },
40867     
40868     getCurrency : function()
40869     {   
40870         var v = this.currencyEl().getValue();
40871         
40872         return v;
40873     },
40874     
40875     restrictHeight : function()
40876     {
40877         this.list.alignTo(this.currencyEl(), this.listAlign);
40878         this.list.alignTo(this.currencyEl(), this.listAlign);
40879     },
40880     
40881     onViewClick : function(view, doFocus, el, e)
40882     {
40883         var index = this.view.getSelectedIndexes()[0];
40884         
40885         var r = this.store.getAt(index);
40886         
40887         if(r){
40888             this.onSelect(r, index);
40889         }
40890     },
40891     
40892     onSelect : function(record, index){
40893         
40894         if(this.fireEvent('beforeselect', this, record, index) !== false){
40895         
40896             this.setFromCurrencyData(index > -1 ? record.data : false);
40897             
40898             this.collapse();
40899             
40900             this.fireEvent('select', this, record, index);
40901         }
40902     },
40903     
40904     setFromCurrencyData : function(o)
40905     {
40906         var currency = '';
40907         
40908         this.lastCurrency = o;
40909         
40910         if (this.currencyField) {
40911             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40912         } else {
40913             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40914         }
40915         
40916         this.lastSelectionText = currency;
40917         
40918         //setting default currency
40919         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40920             this.setCurrency(this.defaultCurrency);
40921             return;
40922         }
40923         
40924         this.setCurrency(currency);
40925     },
40926     
40927     setFromData : function(o)
40928     {
40929         var c = {};
40930         
40931         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40932         
40933         this.setFromCurrencyData(c);
40934         
40935         var value = '';
40936         
40937         if (this.name) {
40938             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40939         } else {
40940             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40941         }
40942         
40943         this.setValue(value);
40944         
40945     },
40946     
40947     setCurrency : function(v)
40948     {   
40949         this.currencyValue = v;
40950         
40951         if(this.rendered){
40952             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40953             this.validate();
40954         }
40955     },
40956     
40957     setValue : function(v)
40958     {
40959         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40960         
40961         this.value = v;
40962         
40963         if(this.rendered){
40964             
40965             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40966             
40967             this.inputEl().dom.value = (v == '') ? '' :
40968                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40969             
40970             if(!this.allowZero && v === '0') {
40971                 this.hiddenEl().dom.value = '';
40972                 this.inputEl().dom.value = '';
40973             }
40974             
40975             this.validate();
40976         }
40977     },
40978     
40979     getRawValue : function()
40980     {
40981         var v = this.inputEl().getValue();
40982         
40983         return v;
40984     },
40985     
40986     getValue : function()
40987     {
40988         return this.fixPrecision(this.parseValue(this.getRawValue()));
40989     },
40990     
40991     parseValue : function(value)
40992     {
40993         if(this.thousandsDelimiter) {
40994             value += "";
40995             r = new RegExp(",", "g");
40996             value = value.replace(r, "");
40997         }
40998         
40999         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41000         return isNaN(value) ? '' : value;
41001         
41002     },
41003     
41004     fixPrecision : function(value)
41005     {
41006         if(this.thousandsDelimiter) {
41007             value += "";
41008             r = new RegExp(",", "g");
41009             value = value.replace(r, "");
41010         }
41011         
41012         var nan = isNaN(value);
41013         
41014         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41015             return nan ? '' : value;
41016         }
41017         return parseFloat(value).toFixed(this.decimalPrecision);
41018     },
41019     
41020     decimalPrecisionFcn : function(v)
41021     {
41022         return Math.floor(v);
41023     },
41024     
41025     validateValue : function(value)
41026     {
41027         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41028             return false;
41029         }
41030         
41031         var num = this.parseValue(value);
41032         
41033         if(isNaN(num)){
41034             this.markInvalid(String.format(this.nanText, value));
41035             return false;
41036         }
41037         
41038         if(num < this.minValue){
41039             this.markInvalid(String.format(this.minText, this.minValue));
41040             return false;
41041         }
41042         
41043         if(num > this.maxValue){
41044             this.markInvalid(String.format(this.maxText, this.maxValue));
41045             return false;
41046         }
41047         
41048         return true;
41049     },
41050     
41051     validate : function()
41052     {
41053         if(this.disabled || this.allowBlank){
41054             this.markValid();
41055             return true;
41056         }
41057         
41058         var currency = this.getCurrency();
41059         
41060         if(this.validateValue(this.getRawValue()) && currency.length){
41061             this.markValid();
41062             return true;
41063         }
41064         
41065         this.markInvalid();
41066         return false;
41067     },
41068     
41069     getName: function()
41070     {
41071         return this.name;
41072     },
41073     
41074     beforeBlur : function()
41075     {
41076         if(!this.castInt){
41077             return;
41078         }
41079         
41080         var v = this.parseValue(this.getRawValue());
41081         
41082         if(v || v == 0){
41083             this.setValue(v);
41084         }
41085     },
41086     
41087     onBlur : function()
41088     {
41089         this.beforeBlur();
41090         
41091         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41092             //this.el.removeClass(this.focusClass);
41093         }
41094         
41095         this.hasFocus = false;
41096         
41097         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41098             this.validate();
41099         }
41100         
41101         var v = this.getValue();
41102         
41103         if(String(v) !== String(this.startValue)){
41104             this.fireEvent('change', this, v, this.startValue);
41105         }
41106         
41107         this.fireEvent("blur", this);
41108     },
41109     
41110     inputEl : function()
41111     {
41112         return this.el.select('.roo-money-amount-input', true).first();
41113     },
41114     
41115     currencyEl : function()
41116     {
41117         return this.el.select('.roo-money-currency-input', true).first();
41118     },
41119     
41120     hiddenEl : function()
41121     {
41122         return this.el.select('input.hidden-number-input',true).first();
41123     }
41124     
41125 });