Roo/Element.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  * 
3934  */
3935
3936 /**
3937  * @class Roo.bootstrap.NavHeaderbar
3938  * @extends Roo.bootstrap.NavSimplebar
3939  * Bootstrap Sidebar class
3940  *
3941  * @cfg {String} brand what is brand
3942  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943  * @cfg {String} brand_href href of the brand
3944  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3945  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3948  * 
3949  * @constructor
3950  * Create a new Sidebar
3951  * @param {Object} config The config object
3952  */
3953
3954
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3957       
3958 };
3959
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3961     
3962     position: '',
3963     brand: '',
3964     brand_href: false,
3965     srButton : true,
3966     autohide : false,
3967     desktopCenter : false,
3968    
3969     
3970     getAutoCreate : function(){
3971         
3972         var   cfg = {
3973             tag: this.nav || 'nav',
3974             cls: 'navbar',
3975             role: 'navigation',
3976             cn: []
3977         };
3978         
3979         var cn = cfg.cn;
3980         if (this.desktopCenter) {
3981             cn.push({cls : 'container', cn : []});
3982             cn = cn[0].cn;
3983         }
3984         
3985         if(this.srButton){
3986             cn.push({
3987                 tag: 'div',
3988                 cls: 'navbar-header',
3989                 cn: [
3990                     {
3991                         tag: 'button',
3992                         type: 'button',
3993                         cls: 'navbar-toggle',
3994                         'data-toggle': 'collapse',
3995                         cn: [
3996                             {
3997                                 tag: 'span',
3998                                 cls: 'sr-only',
3999                                 html: 'Toggle navigation'
4000                             },
4001                             {
4002                                 tag: 'span',
4003                                 cls: 'icon-bar'
4004                             },
4005                             {
4006                                 tag: 'span',
4007                                 cls: 'icon-bar'
4008                             },
4009                             {
4010                                 tag: 'span',
4011                                 cls: 'icon-bar'
4012                             }
4013                         ]
4014                     }
4015                 ]
4016             });
4017         }
4018         
4019         cn.push({
4020             tag: 'div',
4021             cls: 'collapse navbar-collapse',
4022             cn : []
4023         });
4024         
4025         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4026         
4027         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028             cfg.cls += ' navbar-' + this.position;
4029             
4030             // tag can override this..
4031             
4032             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4033         }
4034         
4035         if (this.brand !== '') {
4036             cn[0].cn.push({
4037                 tag: 'a',
4038                 href: this.brand_href ? this.brand_href : '#',
4039                 cls: 'navbar-brand',
4040                 cn: [
4041                 this.brand
4042                 ]
4043             });
4044         }
4045         
4046         if(this.main){
4047             cfg.cls += ' main-nav';
4048         }
4049         
4050         
4051         return cfg;
4052
4053         
4054     },
4055     getHeaderChildContainer : function()
4056     {
4057         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058             return this.el.select('.navbar-header',true).first();
4059         }
4060         
4061         return this.getChildContainer();
4062     },
4063     
4064     
4065     initEvents : function()
4066     {
4067         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4068         
4069         if (this.autohide) {
4070             
4071             var prevScroll = 0;
4072             var ft = this.el;
4073             
4074             Roo.get(document).on('scroll',function(e) {
4075                 var ns = Roo.get(document).getScroll().top;
4076                 var os = prevScroll;
4077                 prevScroll = ns;
4078                 
4079                 if(ns > os){
4080                     ft.removeClass('slideDown');
4081                     ft.addClass('slideUp');
4082                     return;
4083                 }
4084                 ft.removeClass('slideUp');
4085                 ft.addClass('slideDown');
4086                  
4087               
4088           },this);
4089         }
4090     }    
4091     
4092 });
4093
4094
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * navbar
4102  * 
4103  */
4104
4105 /**
4106  * @class Roo.bootstrap.NavSidebar
4107  * @extends Roo.bootstrap.Navbar
4108  * Bootstrap Sidebar class
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavSidebar = function(config){
4117     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4118 };
4119
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4121     
4122     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4123     
4124     getAutoCreate : function(){
4125         
4126         
4127         return  {
4128             tag: 'div',
4129             cls: 'sidebar sidebar-nav'
4130         };
4131     
4132         
4133     }
4134     
4135     
4136     
4137 });
4138
4139
4140
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * nav group
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.NavGroup
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap NavGroup class
4154  * @cfg {String} align (left|right)
4155  * @cfg {Boolean} inverse
4156  * @cfg {String} type (nav|pills|tab) default nav
4157  * @cfg {String} navId - reference Id for navbar.
4158
4159  * 
4160  * @constructor
4161  * Create a new nav group
4162  * @param {Object} config The config object
4163  */
4164
4165 Roo.bootstrap.NavGroup = function(config){
4166     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4167     this.navItems = [];
4168    
4169     Roo.bootstrap.NavGroup.register(this);
4170      this.addEvents({
4171         /**
4172              * @event changed
4173              * Fires when the active item changes
4174              * @param {Roo.bootstrap.NavGroup} this
4175              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4177          */
4178         'changed': true
4179      });
4180     
4181 };
4182
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4184     
4185     align: '',
4186     inverse: false,
4187     form: false,
4188     type: 'nav',
4189     navId : '',
4190     // private
4191     
4192     navItems : false, 
4193     
4194     getAutoCreate : function()
4195     {
4196         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4197         
4198         cfg = {
4199             tag : 'ul',
4200             cls: 'nav' 
4201         };
4202         
4203         if (['tabs','pills'].indexOf(this.type)!==-1) {
4204             cfg.cls += ' nav-' + this.type
4205         } else {
4206             if (this.type!=='nav') {
4207                 Roo.log('nav type must be nav/tabs/pills')
4208             }
4209             cfg.cls += ' navbar-nav'
4210         }
4211         
4212         if (this.parent() && this.parent().sidebar) {
4213             cfg = {
4214                 tag: 'ul',
4215                 cls: 'dashboard-menu sidebar-menu'
4216             };
4217             
4218             return cfg;
4219         }
4220         
4221         if (this.form === true) {
4222             cfg = {
4223                 tag: 'form',
4224                 cls: 'navbar-form'
4225             };
4226             
4227             if (this.align === 'right') {
4228                 cfg.cls += ' navbar-right';
4229             } else {
4230                 cfg.cls += ' navbar-left';
4231             }
4232         }
4233         
4234         if (this.align === 'right') {
4235             cfg.cls += ' navbar-right';
4236         }
4237         
4238         if (this.inverse) {
4239             cfg.cls += ' navbar-inverse';
4240             
4241         }
4242         
4243         
4244         return cfg;
4245     },
4246     /**
4247     * sets the active Navigation item
4248     * @param {Roo.bootstrap.NavItem} the new current navitem
4249     */
4250     setActiveItem : function(item)
4251     {
4252         var prev = false;
4253         Roo.each(this.navItems, function(v){
4254             if (v == item) {
4255                 return ;
4256             }
4257             if (v.isActive()) {
4258                 v.setActive(false, true);
4259                 prev = v;
4260                 
4261             }
4262             
4263         });
4264
4265         item.setActive(true, true);
4266         this.fireEvent('changed', this, item, prev);
4267         
4268         
4269     },
4270     /**
4271     * gets the active Navigation item
4272     * @return {Roo.bootstrap.NavItem} the current navitem
4273     */
4274     getActive : function()
4275     {
4276         
4277         var prev = false;
4278         Roo.each(this.navItems, function(v){
4279             
4280             if (v.isActive()) {
4281                 prev = v;
4282                 
4283             }
4284             
4285         });
4286         return prev;
4287     },
4288     
4289     indexOfNav : function()
4290     {
4291         
4292         var prev = false;
4293         Roo.each(this.navItems, function(v,i){
4294             
4295             if (v.isActive()) {
4296                 prev = i;
4297                 
4298             }
4299             
4300         });
4301         return prev;
4302     },
4303     /**
4304     * adds a Navigation item
4305     * @param {Roo.bootstrap.NavItem} the navitem to add
4306     */
4307     addItem : function(cfg)
4308     {
4309         var cn = new Roo.bootstrap.NavItem(cfg);
4310         this.register(cn);
4311         cn.parentId = this.id;
4312         cn.onRender(this.el, null);
4313         return cn;
4314     },
4315     /**
4316     * register a Navigation item
4317     * @param {Roo.bootstrap.NavItem} the navitem to add
4318     */
4319     register : function(item)
4320     {
4321         this.navItems.push( item);
4322         item.navId = this.navId;
4323     
4324     },
4325     
4326     /**
4327     * clear all the Navigation item
4328     */
4329    
4330     clearAll : function()
4331     {
4332         this.navItems = [];
4333         this.el.dom.innerHTML = '';
4334     },
4335     
4336     getNavItem: function(tabId)
4337     {
4338         var ret = false;
4339         Roo.each(this.navItems, function(e) {
4340             if (e.tabId == tabId) {
4341                ret =  e;
4342                return false;
4343             }
4344             return true;
4345             
4346         });
4347         return ret;
4348     },
4349     
4350     setActiveNext : function()
4351     {
4352         var i = this.indexOfNav(this.getActive());
4353         if (i > this.navItems.length) {
4354             return;
4355         }
4356         this.setActiveItem(this.navItems[i+1]);
4357     },
4358     setActivePrev : function()
4359     {
4360         var i = this.indexOfNav(this.getActive());
4361         if (i  < 1) {
4362             return;
4363         }
4364         this.setActiveItem(this.navItems[i-1]);
4365     },
4366     clearWasActive : function(except) {
4367         Roo.each(this.navItems, function(e) {
4368             if (e.tabId != except.tabId && e.was_active) {
4369                e.was_active = false;
4370                return false;
4371             }
4372             return true;
4373             
4374         });
4375     },
4376     getWasActive : function ()
4377     {
4378         var r = false;
4379         Roo.each(this.navItems, function(e) {
4380             if (e.was_active) {
4381                r = e;
4382                return false;
4383             }
4384             return true;
4385             
4386         });
4387         return r;
4388     }
4389     
4390     
4391 });
4392
4393  
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4395     
4396     groups: {},
4397      /**
4398     * register a Navigation Group
4399     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4400     */
4401     register : function(navgrp)
4402     {
4403         this.groups[navgrp.navId] = navgrp;
4404         
4405     },
4406     /**
4407     * fetch a Navigation Group based on the navigation ID
4408     * @param {string} the navgroup to add
4409     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4410     */
4411     get: function(navId) {
4412         if (typeof(this.groups[navId]) == 'undefined') {
4413             return false;
4414             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4415         }
4416         return this.groups[navId] ;
4417     }
4418     
4419     
4420     
4421 });
4422
4423  /*
4424  * - LGPL
4425  *
4426  * row
4427  * 
4428  */
4429
4430 /**
4431  * @class Roo.bootstrap.NavItem
4432  * @extends Roo.bootstrap.Component
4433  * Bootstrap Navbar.NavItem class
4434  * @cfg {String} href  link to
4435  * @cfg {String} html content of button
4436  * @cfg {String} badge text inside badge
4437  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438  * @cfg {String} glyphicon name of glyphicon
4439  * @cfg {String} icon name of font awesome icon
4440  * @cfg {Boolean} active Is item active
4441  * @cfg {Boolean} disabled Is item disabled
4442  
4443  * @cfg {Boolean} preventDefault (true | false) default false
4444  * @cfg {String} tabId the tab that this item activates.
4445  * @cfg {String} tagtype (a|span) render as a href or span?
4446  * @cfg {Boolean} animateRef (true|false) link to element default false  
4447   
4448  * @constructor
4449  * Create a new Navbar Item
4450  * @param {Object} config The config object
4451  */
4452 Roo.bootstrap.NavItem = function(config){
4453     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4454     this.addEvents({
4455         // raw events
4456         /**
4457          * @event click
4458          * The raw click event for the entire grid.
4459          * @param {Roo.EventObject} e
4460          */
4461         "click" : true,
4462          /**
4463             * @event changed
4464             * Fires when the active item active state changes
4465             * @param {Roo.bootstrap.NavItem} this
4466             * @param {boolean} state the new state
4467              
4468          */
4469         'changed': true,
4470         /**
4471             * @event scrollto
4472             * Fires when scroll to element
4473             * @param {Roo.bootstrap.NavItem} this
4474             * @param {Object} options
4475             * @param {Roo.EventObject} e
4476              
4477          */
4478         'scrollto': true
4479     });
4480    
4481 };
4482
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4484     
4485     href: false,
4486     html: '',
4487     badge: '',
4488     icon: false,
4489     glyphicon: false,
4490     active: false,
4491     preventDefault : false,
4492     tabId : false,
4493     tagtype : 'a',
4494     disabled : false,
4495     animateRef : false,
4496     was_active : false,
4497     
4498     getAutoCreate : function(){
4499          
4500         var cfg = {
4501             tag: 'li',
4502             cls: 'nav-item'
4503             
4504         };
4505         
4506         if (this.active) {
4507             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4508         }
4509         if (this.disabled) {
4510             cfg.cls += ' disabled';
4511         }
4512         
4513         if (this.href || this.html || this.glyphicon || this.icon) {
4514             cfg.cn = [
4515                 {
4516                     tag: this.tagtype,
4517                     href : this.href || "#",
4518                     html: this.html || ''
4519                 }
4520             ];
4521             
4522             if (this.icon) {
4523                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4524             }
4525
4526             if(this.glyphicon) {
4527                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4528             }
4529             
4530             if (this.menu) {
4531                 
4532                 cfg.cn[0].html += " <span class='caret'></span>";
4533              
4534             }
4535             
4536             if (this.badge !== '') {
4537                  
4538                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4539             }
4540         }
4541         
4542         
4543         
4544         return cfg;
4545     },
4546     initEvents: function() 
4547     {
4548         if (typeof (this.menu) != 'undefined') {
4549             this.menu.parentType = this.xtype;
4550             this.menu.triggerEl = this.el;
4551             this.menu = this.addxtype(Roo.apply({}, this.menu));
4552         }
4553         
4554         this.el.select('a',true).on('click', this.onClick, this);
4555         
4556         if(this.tagtype == 'span'){
4557             this.el.select('span',true).on('click', this.onClick, this);
4558         }
4559        
4560         // at this point parent should be available..
4561         this.parent().register(this);
4562     },
4563     
4564     onClick : function(e)
4565     {
4566         if (e.getTarget('.dropdown-menu-item')) {
4567             // did you click on a menu itemm.... - then don't trigger onclick..
4568             return;
4569         }
4570         
4571         if(
4572                 this.preventDefault || 
4573                 this.href == '#' 
4574         ){
4575             Roo.log("NavItem - prevent Default?");
4576             e.preventDefault();
4577         }
4578         
4579         if (this.disabled) {
4580             return;
4581         }
4582         
4583         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584         if (tg && tg.transition) {
4585             Roo.log("waiting for the transitionend");
4586             return;
4587         }
4588         
4589         
4590         
4591         //Roo.log("fire event clicked");
4592         if(this.fireEvent('click', this, e) === false){
4593             return;
4594         };
4595         
4596         if(this.tagtype == 'span'){
4597             return;
4598         }
4599         
4600         //Roo.log(this.href);
4601         var ael = this.el.select('a',true).first();
4602         //Roo.log(ael);
4603         
4604         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607                 return; // ignore... - it's a 'hash' to another page.
4608             }
4609             Roo.log("NavItem - prevent Default?");
4610             e.preventDefault();
4611             this.scrollToElement(e);
4612         }
4613         
4614         
4615         var p =  this.parent();
4616    
4617         if (['tabs','pills'].indexOf(p.type)!==-1) {
4618             if (typeof(p.setActiveItem) !== 'undefined') {
4619                 p.setActiveItem(this);
4620             }
4621         }
4622         
4623         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625             // remove the collapsed menu expand...
4626             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4627         }
4628     },
4629     
4630     isActive: function () {
4631         return this.active
4632     },
4633     setActive : function(state, fire, is_was_active)
4634     {
4635         if (this.active && !state && this.navId) {
4636             this.was_active = true;
4637             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4638             if (nv) {
4639                 nv.clearWasActive(this);
4640             }
4641             
4642         }
4643         this.active = state;
4644         
4645         if (!state ) {
4646             this.el.removeClass('active');
4647         } else if (!this.el.hasClass('active')) {
4648             this.el.addClass('active');
4649         }
4650         if (fire) {
4651             this.fireEvent('changed', this, state);
4652         }
4653         
4654         // show a panel if it's registered and related..
4655         
4656         if (!this.navId || !this.tabId || !state || is_was_active) {
4657             return;
4658         }
4659         
4660         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4661         if (!tg) {
4662             return;
4663         }
4664         var pan = tg.getPanelByName(this.tabId);
4665         if (!pan) {
4666             return;
4667         }
4668         // if we can not flip to new panel - go back to old nav highlight..
4669         if (false == tg.showPanel(pan)) {
4670             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4671             if (nv) {
4672                 var onav = nv.getWasActive();
4673                 if (onav) {
4674                     onav.setActive(true, false, true);
4675                 }
4676             }
4677             
4678         }
4679         
4680         
4681         
4682     },
4683      // this should not be here...
4684     setDisabled : function(state)
4685     {
4686         this.disabled = state;
4687         if (!state ) {
4688             this.el.removeClass('disabled');
4689         } else if (!this.el.hasClass('disabled')) {
4690             this.el.addClass('disabled');
4691         }
4692         
4693     },
4694     
4695     /**
4696      * Fetch the element to display the tooltip on.
4697      * @return {Roo.Element} defaults to this.el
4698      */
4699     tooltipEl : function()
4700     {
4701         return this.el.select('' + this.tagtype + '', true).first();
4702     },
4703     
4704     scrollToElement : function(e)
4705     {
4706         var c = document.body;
4707         
4708         /*
4709          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4710          */
4711         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712             c = document.documentElement;
4713         }
4714         
4715         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4716         
4717         if(!target){
4718             return;
4719         }
4720
4721         var o = target.calcOffsetsTo(c);
4722         
4723         var options = {
4724             target : target,
4725             value : o[1]
4726         };
4727         
4728         this.fireEvent('scrollto', this, options, e);
4729         
4730         Roo.get(c).scrollTo('top', options.value, true);
4731         
4732         return;
4733     }
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * sidebar item
4741  *
4742  *  li
4743  *    <span> icon </span>
4744  *    <span> text </span>
4745  *    <span>badge </span>
4746  */
4747
4748 /**
4749  * @class Roo.bootstrap.NavSidebarItem
4750  * @extends Roo.bootstrap.NavItem
4751  * Bootstrap Navbar.NavSidebarItem class
4752  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753  * {Boolean} open is the menu open
4754  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756  * {String} buttonSize (sm|md|lg)the extra classes for the button
4757  * {Boolean} showArrow show arrow next to the text (default true)
4758  * @constructor
4759  * Create a new Navbar Button
4760  * @param {Object} config The config object
4761  */
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4764     this.addEvents({
4765         // raw events
4766         /**
4767          * @event click
4768          * The raw click event for the entire grid.
4769          * @param {Roo.EventObject} e
4770          */
4771         "click" : true,
4772          /**
4773             * @event changed
4774             * Fires when the active item active state changes
4775             * @param {Roo.bootstrap.NavSidebarItem} this
4776             * @param {boolean} state the new state
4777              
4778          */
4779         'changed': true
4780     });
4781    
4782 };
4783
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4785     
4786     badgeWeight : 'default',
4787     
4788     open: false,
4789     
4790     buttonView : false,
4791     
4792     buttonWeight : 'default',
4793     
4794     buttonSize : 'md',
4795     
4796     showArrow : true,
4797     
4798     getAutoCreate : function(){
4799         
4800         
4801         var a = {
4802                 tag: 'a',
4803                 href : this.href || '#',
4804                 cls: '',
4805                 html : '',
4806                 cn : []
4807         };
4808         
4809         if(this.buttonView){
4810             a = {
4811                 tag: 'button',
4812                 href : this.href || '#',
4813                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4814                 html : this.html,
4815                 cn : []
4816             };
4817         }
4818         
4819         var cfg = {
4820             tag: 'li',
4821             cls: '',
4822             cn: [ a ]
4823         };
4824         
4825         if (this.active) {
4826             cfg.cls += ' active';
4827         }
4828         
4829         if (this.disabled) {
4830             cfg.cls += ' disabled';
4831         }
4832         if (this.open) {
4833             cfg.cls += ' open x-open';
4834         }
4835         // left icon..
4836         if (this.glyphicon || this.icon) {
4837             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4838             a.cn.push({ tag : 'i', cls : c }) ;
4839         }
4840         
4841         if(!this.buttonView){
4842             var span = {
4843                 tag: 'span',
4844                 html : this.html || ''
4845             };
4846
4847             a.cn.push(span);
4848             
4849         }
4850         
4851         if (this.badge !== '') {
4852             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4853         }
4854         
4855         if (this.menu) {
4856             
4857             if(this.showArrow){
4858                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4859             }
4860             
4861             a.cls += ' dropdown-toggle treeview' ;
4862         }
4863         
4864         return cfg;
4865     },
4866     
4867     initEvents : function()
4868     { 
4869         if (typeof (this.menu) != 'undefined') {
4870             this.menu.parentType = this.xtype;
4871             this.menu.triggerEl = this.el;
4872             this.menu = this.addxtype(Roo.apply({}, this.menu));
4873         }
4874         
4875         this.el.on('click', this.onClick, this);
4876         
4877         if(this.badge !== ''){
4878             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4879         }
4880         
4881     },
4882     
4883     onClick : function(e)
4884     {
4885         if(this.disabled){
4886             e.preventDefault();
4887             return;
4888         }
4889         
4890         if(this.preventDefault){
4891             e.preventDefault();
4892         }
4893         
4894         this.fireEvent('click', this);
4895     },
4896     
4897     disable : function()
4898     {
4899         this.setDisabled(true);
4900     },
4901     
4902     enable : function()
4903     {
4904         this.setDisabled(false);
4905     },
4906     
4907     setDisabled : function(state)
4908     {
4909         if(this.disabled == state){
4910             return;
4911         }
4912         
4913         this.disabled = state;
4914         
4915         if (state) {
4916             this.el.addClass('disabled');
4917             return;
4918         }
4919         
4920         this.el.removeClass('disabled');
4921         
4922         return;
4923     },
4924     
4925     setActive : function(state)
4926     {
4927         if(this.active == state){
4928             return;
4929         }
4930         
4931         this.active = state;
4932         
4933         if (state) {
4934             this.el.addClass('active');
4935             return;
4936         }
4937         
4938         this.el.removeClass('active');
4939         
4940         return;
4941     },
4942     
4943     isActive: function () 
4944     {
4945         return this.active;
4946     },
4947     
4948     setBadge : function(str)
4949     {
4950         if(!this.badgeEl){
4951             return;
4952         }
4953         
4954         this.badgeEl.dom.innerHTML = str;
4955     }
4956     
4957    
4958      
4959  
4960 });
4961  
4962
4963  /*
4964  * - LGPL
4965  *
4966  * row
4967  * 
4968  */
4969
4970 /**
4971  * @class Roo.bootstrap.Row
4972  * @extends Roo.bootstrap.Component
4973  * Bootstrap Row class (contains columns...)
4974  * 
4975  * @constructor
4976  * Create a new Row
4977  * @param {Object} config The config object
4978  */
4979
4980 Roo.bootstrap.Row = function(config){
4981     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4982 };
4983
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4985     
4986     getAutoCreate : function(){
4987        return {
4988             cls: 'row clearfix'
4989        };
4990     }
4991     
4992     
4993 });
4994
4995  
4996
4997  /*
4998  * - LGPL
4999  *
5000  * element
5001  * 
5002  */
5003
5004 /**
5005  * @class Roo.bootstrap.Element
5006  * @extends Roo.bootstrap.Component
5007  * Bootstrap Element class
5008  * @cfg {String} html contents of the element
5009  * @cfg {String} tag tag of the element
5010  * @cfg {String} cls class of the element
5011  * @cfg {Boolean} preventDefault (true|false) default false
5012  * @cfg {Boolean} clickable (true|false) default false
5013  * 
5014  * @constructor
5015  * Create a new Element
5016  * @param {Object} config The config object
5017  */
5018
5019 Roo.bootstrap.Element = function(config){
5020     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5021     
5022     this.addEvents({
5023         // raw events
5024         /**
5025          * @event click
5026          * When a element is chick
5027          * @param {Roo.bootstrap.Element} this
5028          * @param {Roo.EventObject} e
5029          */
5030         "click" : true
5031     });
5032 };
5033
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5035     
5036     tag: 'div',
5037     cls: '',
5038     html: '',
5039     preventDefault: false, 
5040     clickable: false,
5041     
5042     getAutoCreate : function(){
5043         
5044         var cfg = {
5045             tag: this.tag,
5046             // cls: this.cls, double assign in parent class Component.js :: onRender
5047             html: this.html
5048         };
5049         
5050         return cfg;
5051     },
5052     
5053     initEvents: function() 
5054     {
5055         Roo.bootstrap.Element.superclass.initEvents.call(this);
5056         
5057         if(this.clickable){
5058             this.el.on('click', this.onClick, this);
5059         }
5060         
5061     },
5062     
5063     onClick : function(e)
5064     {
5065         if(this.preventDefault){
5066             e.preventDefault();
5067         }
5068         
5069         this.fireEvent('click', this, e);
5070     },
5071     
5072     getValue : function()
5073     {
5074         return this.el.dom.innerHTML;
5075     },
5076     
5077     setValue : function(value)
5078     {
5079         this.el.dom.innerHTML = value;
5080     }
5081    
5082 });
5083
5084  
5085
5086  /*
5087  * - LGPL
5088  *
5089  * pagination
5090  * 
5091  */
5092
5093 /**
5094  * @class Roo.bootstrap.Pagination
5095  * @extends Roo.bootstrap.Component
5096  * Bootstrap Pagination class
5097  * @cfg {String} size xs | sm | md | lg
5098  * @cfg {Boolean} inverse false | true
5099  * 
5100  * @constructor
5101  * Create a new Pagination
5102  * @param {Object} config The config object
5103  */
5104
5105 Roo.bootstrap.Pagination = function(config){
5106     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5107 };
5108
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5110     
5111     cls: false,
5112     size: false,
5113     inverse: false,
5114     
5115     getAutoCreate : function(){
5116         var cfg = {
5117             tag: 'ul',
5118                 cls: 'pagination'
5119         };
5120         if (this.inverse) {
5121             cfg.cls += ' inverse';
5122         }
5123         if (this.html) {
5124             cfg.html=this.html;
5125         }
5126         if (this.cls) {
5127             cfg.cls += " " + this.cls;
5128         }
5129         return cfg;
5130     }
5131    
5132 });
5133
5134  
5135
5136  /*
5137  * - LGPL
5138  *
5139  * Pagination item
5140  * 
5141  */
5142
5143
5144 /**
5145  * @class Roo.bootstrap.PaginationItem
5146  * @extends Roo.bootstrap.Component
5147  * Bootstrap PaginationItem class
5148  * @cfg {String} html text
5149  * @cfg {String} href the link
5150  * @cfg {Boolean} preventDefault (true | false) default true
5151  * @cfg {Boolean} active (true | false) default false
5152  * @cfg {Boolean} disabled default false
5153  * 
5154  * 
5155  * @constructor
5156  * Create a new PaginationItem
5157  * @param {Object} config The config object
5158  */
5159
5160
5161 Roo.bootstrap.PaginationItem = function(config){
5162     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5163     this.addEvents({
5164         // raw events
5165         /**
5166          * @event click
5167          * The raw click event for the entire grid.
5168          * @param {Roo.EventObject} e
5169          */
5170         "click" : true
5171     });
5172 };
5173
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5175     
5176     href : false,
5177     html : false,
5178     preventDefault: true,
5179     active : false,
5180     cls : false,
5181     disabled: false,
5182     
5183     getAutoCreate : function(){
5184         var cfg= {
5185             tag: 'li',
5186             cn: [
5187                 {
5188                     tag : 'a',
5189                     href : this.href ? this.href : '#',
5190                     html : this.html ? this.html : ''
5191                 }
5192             ]
5193         };
5194         
5195         if(this.cls){
5196             cfg.cls = this.cls;
5197         }
5198         
5199         if(this.disabled){
5200             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5201         }
5202         
5203         if(this.active){
5204             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5205         }
5206         
5207         return cfg;
5208     },
5209     
5210     initEvents: function() {
5211         
5212         this.el.on('click', this.onClick, this);
5213         
5214     },
5215     onClick : function(e)
5216     {
5217         Roo.log('PaginationItem on click ');
5218         if(this.preventDefault){
5219             e.preventDefault();
5220         }
5221         
5222         if(this.disabled){
5223             return;
5224         }
5225         
5226         this.fireEvent('click', this, e);
5227     }
5228    
5229 });
5230
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * slider
5237  * 
5238  */
5239
5240
5241 /**
5242  * @class Roo.bootstrap.Slider
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Slider class
5245  *    
5246  * @constructor
5247  * Create a new Slider
5248  * @param {Object} config The config object
5249  */
5250
5251 Roo.bootstrap.Slider = function(config){
5252     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5253 };
5254
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5256     
5257     getAutoCreate : function(){
5258         
5259         var cfg = {
5260             tag: 'div',
5261             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5262             cn: [
5263                 {
5264                     tag: 'a',
5265                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5266                 }
5267             ]
5268         };
5269         
5270         return cfg;
5271     }
5272    
5273 });
5274
5275  /*
5276  * Based on:
5277  * Ext JS Library 1.1.1
5278  * Copyright(c) 2006-2007, Ext JS, LLC.
5279  *
5280  * Originally Released Under LGPL - original licence link has changed is not relivant.
5281  *
5282  * Fork - LGPL
5283  * <script type="text/javascript">
5284  */
5285  
5286
5287 /**
5288  * @class Roo.grid.ColumnModel
5289  * @extends Roo.util.Observable
5290  * This is the default implementation of a ColumnModel used by the Grid. It defines
5291  * the columns in the grid.
5292  * <br>Usage:<br>
5293  <pre><code>
5294  var colModel = new Roo.grid.ColumnModel([
5295         {header: "Ticker", width: 60, sortable: true, locked: true},
5296         {header: "Company Name", width: 150, sortable: true},
5297         {header: "Market Cap.", width: 100, sortable: true},
5298         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299         {header: "Employees", width: 100, sortable: true, resizable: false}
5300  ]);
5301  </code></pre>
5302  * <p>
5303  
5304  * The config options listed for this class are options which may appear in each
5305  * individual column definition.
5306  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5307  * @constructor
5308  * @param {Object} config An Array of column config objects. See this class's
5309  * config objects for details.
5310 */
5311 Roo.grid.ColumnModel = function(config){
5312         /**
5313      * The config passed into the constructor
5314      */
5315     this.config = config;
5316     this.lookup = {};
5317
5318     // if no id, create one
5319     // if the column does not have a dataIndex mapping,
5320     // map it to the order it is in the config
5321     for(var i = 0, len = config.length; i < len; i++){
5322         var c = config[i];
5323         if(typeof c.dataIndex == "undefined"){
5324             c.dataIndex = i;
5325         }
5326         if(typeof c.renderer == "string"){
5327             c.renderer = Roo.util.Format[c.renderer];
5328         }
5329         if(typeof c.id == "undefined"){
5330             c.id = Roo.id();
5331         }
5332         if(c.editor && c.editor.xtype){
5333             c.editor  = Roo.factory(c.editor, Roo.grid);
5334         }
5335         if(c.editor && c.editor.isFormField){
5336             c.editor = new Roo.grid.GridEditor(c.editor);
5337         }
5338         this.lookup[c.id] = c;
5339     }
5340
5341     /**
5342      * The width of columns which have no width specified (defaults to 100)
5343      * @type Number
5344      */
5345     this.defaultWidth = 100;
5346
5347     /**
5348      * Default sortable of columns which have no sortable specified (defaults to false)
5349      * @type Boolean
5350      */
5351     this.defaultSortable = false;
5352
5353     this.addEvents({
5354         /**
5355              * @event widthchange
5356              * Fires when the width of a column changes.
5357              * @param {ColumnModel} this
5358              * @param {Number} columnIndex The column index
5359              * @param {Number} newWidth The new width
5360              */
5361             "widthchange": true,
5362         /**
5363              * @event headerchange
5364              * Fires when the text of a header changes.
5365              * @param {ColumnModel} this
5366              * @param {Number} columnIndex The column index
5367              * @param {Number} newText The new header text
5368              */
5369             "headerchange": true,
5370         /**
5371              * @event hiddenchange
5372              * Fires when a column is hidden or "unhidden".
5373              * @param {ColumnModel} this
5374              * @param {Number} columnIndex The column index
5375              * @param {Boolean} hidden true if hidden, false otherwise
5376              */
5377             "hiddenchange": true,
5378             /**
5379          * @event columnmoved
5380          * Fires when a column is moved.
5381          * @param {ColumnModel} this
5382          * @param {Number} oldIndex
5383          * @param {Number} newIndex
5384          */
5385         "columnmoved" : true,
5386         /**
5387          * @event columlockchange
5388          * Fires when a column's locked state is changed
5389          * @param {ColumnModel} this
5390          * @param {Number} colIndex
5391          * @param {Boolean} locked true if locked
5392          */
5393         "columnlockchange" : true
5394     });
5395     Roo.grid.ColumnModel.superclass.constructor.call(this);
5396 };
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5398     /**
5399      * @cfg {String} header The header text to display in the Grid view.
5400      */
5401     /**
5402      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404      * specified, the column's index is used as an index into the Record's data Array.
5405      */
5406     /**
5407      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5409      */
5410     /**
5411      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412      * Defaults to the value of the {@link #defaultSortable} property.
5413      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5414      */
5415     /**
5416      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5417      */
5418     /**
5419      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5420      */
5421     /**
5422      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5423      */
5424     /**
5425      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5426      */
5427     /**
5428      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5432      */
5433        /**
5434      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5435      */
5436     /**
5437      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5438      */
5439     /**
5440      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5441      */
5442     /**
5443      * @cfg {String} cursor (Optional)
5444      */
5445     /**
5446      * @cfg {String} tooltip (Optional)
5447      */
5448     /**
5449      * @cfg {Number} xs (Optional)
5450      */
5451     /**
5452      * @cfg {Number} sm (Optional)
5453      */
5454     /**
5455      * @cfg {Number} md (Optional)
5456      */
5457     /**
5458      * @cfg {Number} lg (Optional)
5459      */
5460     /**
5461      * Returns the id of the column at the specified index.
5462      * @param {Number} index The column index
5463      * @return {String} the id
5464      */
5465     getColumnId : function(index){
5466         return this.config[index].id;
5467     },
5468
5469     /**
5470      * Returns the column for a specified id.
5471      * @param {String} id The column id
5472      * @return {Object} the column
5473      */
5474     getColumnById : function(id){
5475         return this.lookup[id];
5476     },
5477
5478     
5479     /**
5480      * Returns the column for a specified dataIndex.
5481      * @param {String} dataIndex The column dataIndex
5482      * @return {Object|Boolean} the column or false if not found
5483      */
5484     getColumnByDataIndex: function(dataIndex){
5485         var index = this.findColumnIndex(dataIndex);
5486         return index > -1 ? this.config[index] : false;
5487     },
5488     
5489     /**
5490      * Returns the index for a specified column id.
5491      * @param {String} id The column id
5492      * @return {Number} the index, or -1 if not found
5493      */
5494     getIndexById : function(id){
5495         for(var i = 0, len = this.config.length; i < len; i++){
5496             if(this.config[i].id == id){
5497                 return i;
5498             }
5499         }
5500         return -1;
5501     },
5502     
5503     /**
5504      * Returns the index for a specified column dataIndex.
5505      * @param {String} dataIndex The column dataIndex
5506      * @return {Number} the index, or -1 if not found
5507      */
5508     
5509     findColumnIndex : function(dataIndex){
5510         for(var i = 0, len = this.config.length; i < len; i++){
5511             if(this.config[i].dataIndex == dataIndex){
5512                 return i;
5513             }
5514         }
5515         return -1;
5516     },
5517     
5518     
5519     moveColumn : function(oldIndex, newIndex){
5520         var c = this.config[oldIndex];
5521         this.config.splice(oldIndex, 1);
5522         this.config.splice(newIndex, 0, c);
5523         this.dataMap = null;
5524         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5525     },
5526
5527     isLocked : function(colIndex){
5528         return this.config[colIndex].locked === true;
5529     },
5530
5531     setLocked : function(colIndex, value, suppressEvent){
5532         if(this.isLocked(colIndex) == value){
5533             return;
5534         }
5535         this.config[colIndex].locked = value;
5536         if(!suppressEvent){
5537             this.fireEvent("columnlockchange", this, colIndex, value);
5538         }
5539     },
5540
5541     getTotalLockedWidth : function(){
5542         var totalWidth = 0;
5543         for(var i = 0; i < this.config.length; i++){
5544             if(this.isLocked(i) && !this.isHidden(i)){
5545                 this.totalWidth += this.getColumnWidth(i);
5546             }
5547         }
5548         return totalWidth;
5549     },
5550
5551     getLockedCount : function(){
5552         for(var i = 0, len = this.config.length; i < len; i++){
5553             if(!this.isLocked(i)){
5554                 return i;
5555             }
5556         }
5557         
5558         return this.config.length;
5559     },
5560
5561     /**
5562      * Returns the number of columns.
5563      * @return {Number}
5564      */
5565     getColumnCount : function(visibleOnly){
5566         if(visibleOnly === true){
5567             var c = 0;
5568             for(var i = 0, len = this.config.length; i < len; i++){
5569                 if(!this.isHidden(i)){
5570                     c++;
5571                 }
5572             }
5573             return c;
5574         }
5575         return this.config.length;
5576     },
5577
5578     /**
5579      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580      * @param {Function} fn
5581      * @param {Object} scope (optional)
5582      * @return {Array} result
5583      */
5584     getColumnsBy : function(fn, scope){
5585         var r = [];
5586         for(var i = 0, len = this.config.length; i < len; i++){
5587             var c = this.config[i];
5588             if(fn.call(scope||this, c, i) === true){
5589                 r[r.length] = c;
5590             }
5591         }
5592         return r;
5593     },
5594
5595     /**
5596      * Returns true if the specified column is sortable.
5597      * @param {Number} col The column index
5598      * @return {Boolean}
5599      */
5600     isSortable : function(col){
5601         if(typeof this.config[col].sortable == "undefined"){
5602             return this.defaultSortable;
5603         }
5604         return this.config[col].sortable;
5605     },
5606
5607     /**
5608      * Returns the rendering (formatting) function defined for the column.
5609      * @param {Number} col The column index.
5610      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5611      */
5612     getRenderer : function(col){
5613         if(!this.config[col].renderer){
5614             return Roo.grid.ColumnModel.defaultRenderer;
5615         }
5616         return this.config[col].renderer;
5617     },
5618
5619     /**
5620      * Sets the rendering (formatting) function for a column.
5621      * @param {Number} col The column index
5622      * @param {Function} fn The function to use to process the cell's raw data
5623      * to return HTML markup for the grid view. The render function is called with
5624      * the following parameters:<ul>
5625      * <li>Data value.</li>
5626      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627      * <li>css A CSS style string to apply to the table cell.</li>
5628      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630      * <li>Row index</li>
5631      * <li>Column index</li>
5632      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5633      */
5634     setRenderer : function(col, fn){
5635         this.config[col].renderer = fn;
5636     },
5637
5638     /**
5639      * Returns the width for the specified column.
5640      * @param {Number} col The column index
5641      * @return {Number}
5642      */
5643     getColumnWidth : function(col){
5644         return this.config[col].width * 1 || this.defaultWidth;
5645     },
5646
5647     /**
5648      * Sets the width for a column.
5649      * @param {Number} col The column index
5650      * @param {Number} width The new width
5651      */
5652     setColumnWidth : function(col, width, suppressEvent){
5653         this.config[col].width = width;
5654         this.totalWidth = null;
5655         if(!suppressEvent){
5656              this.fireEvent("widthchange", this, col, width);
5657         }
5658     },
5659
5660     /**
5661      * Returns the total width of all columns.
5662      * @param {Boolean} includeHidden True to include hidden column widths
5663      * @return {Number}
5664      */
5665     getTotalWidth : function(includeHidden){
5666         if(!this.totalWidth){
5667             this.totalWidth = 0;
5668             for(var i = 0, len = this.config.length; i < len; i++){
5669                 if(includeHidden || !this.isHidden(i)){
5670                     this.totalWidth += this.getColumnWidth(i);
5671                 }
5672             }
5673         }
5674         return this.totalWidth;
5675     },
5676
5677     /**
5678      * Returns the header for the specified column.
5679      * @param {Number} col The column index
5680      * @return {String}
5681      */
5682     getColumnHeader : function(col){
5683         return this.config[col].header;
5684     },
5685
5686     /**
5687      * Sets the header for a column.
5688      * @param {Number} col The column index
5689      * @param {String} header The new header
5690      */
5691     setColumnHeader : function(col, header){
5692         this.config[col].header = header;
5693         this.fireEvent("headerchange", this, col, header);
5694     },
5695
5696     /**
5697      * Returns the tooltip for the specified column.
5698      * @param {Number} col The column index
5699      * @return {String}
5700      */
5701     getColumnTooltip : function(col){
5702             return this.config[col].tooltip;
5703     },
5704     /**
5705      * Sets the tooltip for a column.
5706      * @param {Number} col The column index
5707      * @param {String} tooltip The new tooltip
5708      */
5709     setColumnTooltip : function(col, tooltip){
5710             this.config[col].tooltip = tooltip;
5711     },
5712
5713     /**
5714      * Returns the dataIndex for the specified column.
5715      * @param {Number} col The column index
5716      * @return {Number}
5717      */
5718     getDataIndex : function(col){
5719         return this.config[col].dataIndex;
5720     },
5721
5722     /**
5723      * Sets the dataIndex for a column.
5724      * @param {Number} col The column index
5725      * @param {Number} dataIndex The new dataIndex
5726      */
5727     setDataIndex : function(col, dataIndex){
5728         this.config[col].dataIndex = dataIndex;
5729     },
5730
5731     
5732     
5733     /**
5734      * Returns true if the cell is editable.
5735      * @param {Number} colIndex The column index
5736      * @param {Number} rowIndex The row index - this is nto actually used..?
5737      * @return {Boolean}
5738      */
5739     isCellEditable : function(colIndex, rowIndex){
5740         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5741     },
5742
5743     /**
5744      * Returns the editor defined for the cell/column.
5745      * return false or null to disable editing.
5746      * @param {Number} colIndex The column index
5747      * @param {Number} rowIndex The row index
5748      * @return {Object}
5749      */
5750     getCellEditor : function(colIndex, rowIndex){
5751         return this.config[colIndex].editor;
5752     },
5753
5754     /**
5755      * Sets if a column is editable.
5756      * @param {Number} col The column index
5757      * @param {Boolean} editable True if the column is editable
5758      */
5759     setEditable : function(col, editable){
5760         this.config[col].editable = editable;
5761     },
5762
5763
5764     /**
5765      * Returns true if the column is hidden.
5766      * @param {Number} colIndex The column index
5767      * @return {Boolean}
5768      */
5769     isHidden : function(colIndex){
5770         return this.config[colIndex].hidden;
5771     },
5772
5773
5774     /**
5775      * Returns true if the column width cannot be changed
5776      */
5777     isFixed : function(colIndex){
5778         return this.config[colIndex].fixed;
5779     },
5780
5781     /**
5782      * Returns true if the column can be resized
5783      * @return {Boolean}
5784      */
5785     isResizable : function(colIndex){
5786         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5787     },
5788     /**
5789      * Sets if a column is hidden.
5790      * @param {Number} colIndex The column index
5791      * @param {Boolean} hidden True if the column is hidden
5792      */
5793     setHidden : function(colIndex, hidden){
5794         this.config[colIndex].hidden = hidden;
5795         this.totalWidth = null;
5796         this.fireEvent("hiddenchange", this, colIndex, hidden);
5797     },
5798
5799     /**
5800      * Sets the editor for a column.
5801      * @param {Number} col The column index
5802      * @param {Object} editor The editor object
5803      */
5804     setEditor : function(col, editor){
5805         this.config[col].editor = editor;
5806     }
5807 });
5808
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5810 {
5811     if(typeof value == "object") {
5812         return value;
5813     }
5814         if(typeof value == "string" && value.length < 1){
5815             return "&#160;";
5816         }
5817     
5818         return String.format("{0}", value);
5819 };
5820
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5823 /*
5824  * Based on:
5825  * Ext JS Library 1.1.1
5826  * Copyright(c) 2006-2007, Ext JS, LLC.
5827  *
5828  * Originally Released Under LGPL - original licence link has changed is not relivant.
5829  *
5830  * Fork - LGPL
5831  * <script type="text/javascript">
5832  */
5833  
5834 /**
5835  * @class Roo.LoadMask
5836  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5837  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5839  * element's UpdateManager load indicator and will be destroyed after the initial load.
5840  * @constructor
5841  * Create a new LoadMask
5842  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843  * @param {Object} config The config object
5844  */
5845 Roo.LoadMask = function(el, config){
5846     this.el = Roo.get(el);
5847     Roo.apply(this, config);
5848     if(this.store){
5849         this.store.on('beforeload', this.onBeforeLoad, this);
5850         this.store.on('load', this.onLoad, this);
5851         this.store.on('loadexception', this.onLoadException, this);
5852         this.removeMask = false;
5853     }else{
5854         var um = this.el.getUpdateManager();
5855         um.showLoadIndicator = false; // disable the default indicator
5856         um.on('beforeupdate', this.onBeforeLoad, this);
5857         um.on('update', this.onLoad, this);
5858         um.on('failure', this.onLoad, this);
5859         this.removeMask = true;
5860     }
5861 };
5862
5863 Roo.LoadMask.prototype = {
5864     /**
5865      * @cfg {Boolean} removeMask
5866      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5868      */
5869     /**
5870      * @cfg {String} msg
5871      * The text to display in a centered loading message box (defaults to 'Loading...')
5872      */
5873     msg : 'Loading...',
5874     /**
5875      * @cfg {String} msgCls
5876      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5877      */
5878     msgCls : 'x-mask-loading',
5879
5880     /**
5881      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5882      * @type Boolean
5883      */
5884     disabled: false,
5885
5886     /**
5887      * Disables the mask to prevent it from being displayed
5888      */
5889     disable : function(){
5890        this.disabled = true;
5891     },
5892
5893     /**
5894      * Enables the mask so that it can be displayed
5895      */
5896     enable : function(){
5897         this.disabled = false;
5898     },
5899     
5900     onLoadException : function()
5901     {
5902         Roo.log(arguments);
5903         
5904         if (typeof(arguments[3]) != 'undefined') {
5905             Roo.MessageBox.alert("Error loading",arguments[3]);
5906         } 
5907         /*
5908         try {
5909             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5911             }   
5912         } catch(e) {
5913             
5914         }
5915         */
5916     
5917         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5918     },
5919     // private
5920     onLoad : function()
5921     {
5922         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923     },
5924
5925     // private
5926     onBeforeLoad : function(){
5927         if(!this.disabled){
5928             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5929         }
5930     },
5931
5932     // private
5933     destroy : function(){
5934         if(this.store){
5935             this.store.un('beforeload', this.onBeforeLoad, this);
5936             this.store.un('load', this.onLoad, this);
5937             this.store.un('loadexception', this.onLoadException, this);
5938         }else{
5939             var um = this.el.getUpdateManager();
5940             um.un('beforeupdate', this.onBeforeLoad, this);
5941             um.un('update', this.onLoad, this);
5942             um.un('failure', this.onLoad, this);
5943         }
5944     }
5945 };/*
5946  * - LGPL
5947  *
5948  * table
5949  * 
5950  */
5951
5952 /**
5953  * @class Roo.bootstrap.Table
5954  * @extends Roo.bootstrap.Component
5955  * Bootstrap Table class
5956  * @cfg {String} cls table class
5957  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958  * @cfg {String} bgcolor Specifies the background color for a table
5959  * @cfg {Number} border Specifies whether the table cells should have borders or not
5960  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961  * @cfg {Number} cellspacing Specifies the space between cells
5962  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964  * @cfg {String} sortable Specifies that the table should be sortable
5965  * @cfg {String} summary Specifies a summary of the content of a table
5966  * @cfg {Number} width Specifies the width of a table
5967  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5968  * 
5969  * @cfg {boolean} striped Should the rows be alternative striped
5970  * @cfg {boolean} bordered Add borders to the table
5971  * @cfg {boolean} hover Add hover highlighting
5972  * @cfg {boolean} condensed Format condensed
5973  * @cfg {boolean} responsive Format condensed
5974  * @cfg {Boolean} loadMask (true|false) default false
5975  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977  * @cfg {Boolean} rowSelection (true|false) default false
5978  * @cfg {Boolean} cellSelection (true|false) default false
5979  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5981  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5982  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5983  
5984  * 
5985  * @constructor
5986  * Create a new Table
5987  * @param {Object} config The config object
5988  */
5989
5990 Roo.bootstrap.Table = function(config){
5991     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5992     
5993   
5994     
5995     // BC...
5996     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6000     
6001     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6002     if (this.sm) {
6003         this.sm.grid = this;
6004         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005         this.sm = this.selModel;
6006         this.sm.xmodule = this.xmodule || false;
6007     }
6008     
6009     if (this.cm && typeof(this.cm.config) == 'undefined') {
6010         this.colModel = new Roo.grid.ColumnModel(this.cm);
6011         this.cm = this.colModel;
6012         this.cm.xmodule = this.xmodule || false;
6013     }
6014     if (this.store) {
6015         this.store= Roo.factory(this.store, Roo.data);
6016         this.ds = this.store;
6017         this.ds.xmodule = this.xmodule || false;
6018          
6019     }
6020     if (this.footer && this.store) {
6021         this.footer.dataSource = this.ds;
6022         this.footer = Roo.factory(this.footer);
6023     }
6024     
6025     /** @private */
6026     this.addEvents({
6027         /**
6028          * @event cellclick
6029          * Fires when a cell is clicked
6030          * @param {Roo.bootstrap.Table} this
6031          * @param {Roo.Element} el
6032          * @param {Number} rowIndex
6033          * @param {Number} columnIndex
6034          * @param {Roo.EventObject} e
6035          */
6036         "cellclick" : true,
6037         /**
6038          * @event celldblclick
6039          * Fires when a cell is double clicked
6040          * @param {Roo.bootstrap.Table} this
6041          * @param {Roo.Element} el
6042          * @param {Number} rowIndex
6043          * @param {Number} columnIndex
6044          * @param {Roo.EventObject} e
6045          */
6046         "celldblclick" : true,
6047         /**
6048          * @event rowclick
6049          * Fires when a row is clicked
6050          * @param {Roo.bootstrap.Table} this
6051          * @param {Roo.Element} el
6052          * @param {Number} rowIndex
6053          * @param {Roo.EventObject} e
6054          */
6055         "rowclick" : true,
6056         /**
6057          * @event rowdblclick
6058          * Fires when a row is double clicked
6059          * @param {Roo.bootstrap.Table} this
6060          * @param {Roo.Element} el
6061          * @param {Number} rowIndex
6062          * @param {Roo.EventObject} e
6063          */
6064         "rowdblclick" : true,
6065         /**
6066          * @event mouseover
6067          * Fires when a mouseover occur
6068          * @param {Roo.bootstrap.Table} this
6069          * @param {Roo.Element} el
6070          * @param {Number} rowIndex
6071          * @param {Number} columnIndex
6072          * @param {Roo.EventObject} e
6073          */
6074         "mouseover" : true,
6075         /**
6076          * @event mouseout
6077          * Fires when a mouseout occur
6078          * @param {Roo.bootstrap.Table} this
6079          * @param {Roo.Element} el
6080          * @param {Number} rowIndex
6081          * @param {Number} columnIndex
6082          * @param {Roo.EventObject} e
6083          */
6084         "mouseout" : true,
6085         /**
6086          * @event rowclass
6087          * Fires when a row is rendered, so you can change add a style to it.
6088          * @param {Roo.bootstrap.Table} this
6089          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6090          */
6091         'rowclass' : true,
6092           /**
6093          * @event rowsrendered
6094          * Fires when all the  rows have been rendered
6095          * @param {Roo.bootstrap.Table} this
6096          */
6097         'rowsrendered' : true,
6098         /**
6099          * @event contextmenu
6100          * The raw contextmenu event for the entire grid.
6101          * @param {Roo.EventObject} e
6102          */
6103         "contextmenu" : true,
6104         /**
6105          * @event rowcontextmenu
6106          * Fires when a row is right clicked
6107          * @param {Roo.bootstrap.Table} this
6108          * @param {Number} rowIndex
6109          * @param {Roo.EventObject} e
6110          */
6111         "rowcontextmenu" : true,
6112         /**
6113          * @event cellcontextmenu
6114          * Fires when a cell is right clicked
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Number} rowIndex
6117          * @param {Number} cellIndex
6118          * @param {Roo.EventObject} e
6119          */
6120          "cellcontextmenu" : true,
6121          /**
6122          * @event headercontextmenu
6123          * Fires when a header is right clicked
6124          * @param {Roo.bootstrap.Table} this
6125          * @param {Number} columnIndex
6126          * @param {Roo.EventObject} e
6127          */
6128         "headercontextmenu" : true
6129     });
6130 };
6131
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6133     
6134     cls: false,
6135     align: false,
6136     bgcolor: false,
6137     border: false,
6138     cellpadding: false,
6139     cellspacing: false,
6140     frame: false,
6141     rules: false,
6142     sortable: false,
6143     summary: false,
6144     width: false,
6145     striped : false,
6146     scrollBody : false,
6147     bordered: false,
6148     hover:  false,
6149     condensed : false,
6150     responsive : false,
6151     sm : false,
6152     cm : false,
6153     store : false,
6154     loadMask : false,
6155     footerShow : true,
6156     headerShow : true,
6157   
6158     rowSelection : false,
6159     cellSelection : false,
6160     layout : false,
6161     
6162     // Roo.Element - the tbody
6163     mainBody: false,
6164     // Roo.Element - thead element
6165     mainHead: false,
6166     
6167     container: false, // used by gridpanel...
6168     
6169     lazyLoad : false,
6170     
6171     CSS : Roo.util.CSS,
6172     
6173     auto_hide_footer : false,
6174     
6175     getAutoCreate : function()
6176     {
6177         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'table',
6181             cls : 'table',
6182             cn : []
6183         };
6184         if (this.scrollBody) {
6185             cfg.cls += ' table-body-fixed';
6186         }    
6187         if (this.striped) {
6188             cfg.cls += ' table-striped';
6189         }
6190         
6191         if (this.hover) {
6192             cfg.cls += ' table-hover';
6193         }
6194         if (this.bordered) {
6195             cfg.cls += ' table-bordered';
6196         }
6197         if (this.condensed) {
6198             cfg.cls += ' table-condensed';
6199         }
6200         if (this.responsive) {
6201             cfg.cls += ' table-responsive';
6202         }
6203         
6204         if (this.cls) {
6205             cfg.cls+=  ' ' +this.cls;
6206         }
6207         
6208         // this lot should be simplifed...
6209         var _t = this;
6210         var cp = [
6211             'align',
6212             'bgcolor',
6213             'border',
6214             'cellpadding',
6215             'cellspacing',
6216             'frame',
6217             'rules',
6218             'sortable',
6219             'summary',
6220             'width'
6221         ].forEach(function(k) {
6222             if (_t[k]) {
6223                 cfg[k] = _t[k];
6224             }
6225         });
6226         
6227         
6228         if (this.layout) {
6229             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6230         }
6231         
6232         if(this.store || this.cm){
6233             if(this.headerShow){
6234                 cfg.cn.push(this.renderHeader());
6235             }
6236             
6237             cfg.cn.push(this.renderBody());
6238             
6239             if(this.footerShow){
6240                 cfg.cn.push(this.renderFooter());
6241             }
6242             // where does this come from?
6243             //cfg.cls+=  ' TableGrid';
6244         }
6245         
6246         return { cn : [ cfg ] };
6247     },
6248     
6249     initEvents : function()
6250     {   
6251         if(!this.store || !this.cm){
6252             return;
6253         }
6254         if (this.selModel) {
6255             this.selModel.initEvents();
6256         }
6257         
6258         
6259         //Roo.log('initEvents with ds!!!!');
6260         
6261         this.mainBody = this.el.select('tbody', true).first();
6262         this.mainHead = this.el.select('thead', true).first();
6263         this.mainFoot = this.el.select('tfoot', true).first();
6264         
6265         
6266         
6267         var _this = this;
6268         
6269         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270             e.on('click', _this.sort, _this);
6271         });
6272         
6273         this.mainBody.on("click", this.onClick, this);
6274         this.mainBody.on("dblclick", this.onDblClick, this);
6275         
6276         // why is this done????? = it breaks dialogs??
6277         //this.parent().el.setStyle('position', 'relative');
6278         
6279         
6280         if (this.footer) {
6281             this.footer.parentId = this.id;
6282             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6283             
6284             if(this.lazyLoad){
6285                 this.el.select('tfoot tr td').first().addClass('hide');
6286             }
6287         } 
6288         
6289         if(this.loadMask) {
6290             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6291         }
6292         
6293         this.store.on('load', this.onLoad, this);
6294         this.store.on('beforeload', this.onBeforeLoad, this);
6295         this.store.on('update', this.onUpdate, this);
6296         this.store.on('add', this.onAdd, this);
6297         this.store.on("clear", this.clear, this);
6298         
6299         this.el.on("contextmenu", this.onContextMenu, this);
6300         
6301         this.mainBody.on('scroll', this.onBodyScroll, this);
6302         
6303         this.cm.on("headerchange", this.onHeaderChange, this);
6304         
6305         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6306         
6307     },
6308     
6309     onContextMenu : function(e, t)
6310     {
6311         this.processEvent("contextmenu", e);
6312     },
6313     
6314     processEvent : function(name, e)
6315     {
6316         if (name != 'touchstart' ) {
6317             this.fireEvent(name, e);    
6318         }
6319         
6320         var t = e.getTarget();
6321         
6322         var cell = Roo.get(t);
6323         
6324         if(!cell){
6325             return;
6326         }
6327         
6328         if(cell.findParent('tfoot', false, true)){
6329             return;
6330         }
6331         
6332         if(cell.findParent('thead', false, true)){
6333             
6334             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335                 cell = Roo.get(t).findParent('th', false, true);
6336                 if (!cell) {
6337                     Roo.log("failed to find th in thead?");
6338                     Roo.log(e.getTarget());
6339                     return;
6340                 }
6341             }
6342             
6343             var cellIndex = cell.dom.cellIndex;
6344             
6345             var ename = name == 'touchstart' ? 'click' : name;
6346             this.fireEvent("header" + ename, this, cellIndex, e);
6347             
6348             return;
6349         }
6350         
6351         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352             cell = Roo.get(t).findParent('td', false, true);
6353             if (!cell) {
6354                 Roo.log("failed to find th in tbody?");
6355                 Roo.log(e.getTarget());
6356                 return;
6357             }
6358         }
6359         
6360         var row = cell.findParent('tr', false, true);
6361         var cellIndex = cell.dom.cellIndex;
6362         var rowIndex = row.dom.rowIndex - 1;
6363         
6364         if(row !== false){
6365             
6366             this.fireEvent("row" + name, this, rowIndex, e);
6367             
6368             if(cell !== false){
6369             
6370                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6371             }
6372         }
6373         
6374     },
6375     
6376     onMouseover : function(e, el)
6377     {
6378         var cell = Roo.get(el);
6379         
6380         if(!cell){
6381             return;
6382         }
6383         
6384         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385             cell = cell.findParent('td', false, true);
6386         }
6387         
6388         var row = cell.findParent('tr', false, true);
6389         var cellIndex = cell.dom.cellIndex;
6390         var rowIndex = row.dom.rowIndex - 1; // start from 0
6391         
6392         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6393         
6394     },
6395     
6396     onMouseout : function(e, el)
6397     {
6398         var cell = Roo.get(el);
6399         
6400         if(!cell){
6401             return;
6402         }
6403         
6404         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405             cell = cell.findParent('td', false, true);
6406         }
6407         
6408         var row = cell.findParent('tr', false, true);
6409         var cellIndex = cell.dom.cellIndex;
6410         var rowIndex = row.dom.rowIndex - 1; // start from 0
6411         
6412         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6413         
6414     },
6415     
6416     onClick : function(e, el)
6417     {
6418         var cell = Roo.get(el);
6419         
6420         if(!cell || (!this.cellSelection && !this.rowSelection)){
6421             return;
6422         }
6423         
6424         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425             cell = cell.findParent('td', false, true);
6426         }
6427         
6428         if(!cell || typeof(cell) == 'undefined'){
6429             return;
6430         }
6431         
6432         var row = cell.findParent('tr', false, true);
6433         
6434         if(!row || typeof(row) == 'undefined'){
6435             return;
6436         }
6437         
6438         var cellIndex = cell.dom.cellIndex;
6439         var rowIndex = this.getRowIndex(row);
6440         
6441         // why??? - should these not be based on SelectionModel?
6442         if(this.cellSelection){
6443             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6444         }
6445         
6446         if(this.rowSelection){
6447             this.fireEvent('rowclick', this, row, rowIndex, e);
6448         }
6449         
6450         
6451     },
6452         
6453     onDblClick : function(e,el)
6454     {
6455         var cell = Roo.get(el);
6456         
6457         if(!cell || (!this.cellSelection && !this.rowSelection)){
6458             return;
6459         }
6460         
6461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462             cell = cell.findParent('td', false, true);
6463         }
6464         
6465         if(!cell || typeof(cell) == 'undefined'){
6466             return;
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         
6471         if(!row || typeof(row) == 'undefined'){
6472             return;
6473         }
6474         
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = this.getRowIndex(row);
6477         
6478         if(this.cellSelection){
6479             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6480         }
6481         
6482         if(this.rowSelection){
6483             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6484         }
6485     },
6486     
6487     sort : function(e,el)
6488     {
6489         var col = Roo.get(el);
6490         
6491         if(!col.hasClass('sortable')){
6492             return;
6493         }
6494         
6495         var sort = col.attr('sort');
6496         var dir = 'ASC';
6497         
6498         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6499             dir = 'DESC';
6500         }
6501         
6502         this.store.sortInfo = {field : sort, direction : dir};
6503         
6504         if (this.footer) {
6505             Roo.log("calling footer first");
6506             this.footer.onClick('first');
6507         } else {
6508         
6509             this.store.load({ params : { start : 0 } });
6510         }
6511     },
6512     
6513     renderHeader : function()
6514     {
6515         var header = {
6516             tag: 'thead',
6517             cn : []
6518         };
6519         
6520         var cm = this.cm;
6521         this.totalWidth = 0;
6522         
6523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6524             
6525             var config = cm.config[i];
6526             
6527             var c = {
6528                 tag: 'th',
6529                 cls : 'x-hcol-' + i,
6530                 style : '',
6531                 html: cm.getColumnHeader(i)
6532             };
6533             
6534             var hh = '';
6535             
6536             if(typeof(config.sortable) != 'undefined' && config.sortable){
6537                 c.cls = 'sortable';
6538                 c.html = '<i class="glyphicon"></i>' + c.html;
6539             }
6540             
6541             if(typeof(config.lgHeader) != 'undefined'){
6542                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6543             }
6544             
6545             if(typeof(config.mdHeader) != 'undefined'){
6546                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6547             }
6548             
6549             if(typeof(config.smHeader) != 'undefined'){
6550                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6551             }
6552             
6553             if(typeof(config.xsHeader) != 'undefined'){
6554                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6555             }
6556             
6557             if(hh.length){
6558                 c.html = hh;
6559             }
6560             
6561             if(typeof(config.tooltip) != 'undefined'){
6562                 c.tooltip = config.tooltip;
6563             }
6564             
6565             if(typeof(config.colspan) != 'undefined'){
6566                 c.colspan = config.colspan;
6567             }
6568             
6569             if(typeof(config.hidden) != 'undefined' && config.hidden){
6570                 c.style += ' display:none;';
6571             }
6572             
6573             if(typeof(config.dataIndex) != 'undefined'){
6574                 c.sort = config.dataIndex;
6575             }
6576             
6577            
6578             
6579             if(typeof(config.align) != 'undefined' && config.align.length){
6580                 c.style += ' text-align:' + config.align + ';';
6581             }
6582             
6583             if(typeof(config.width) != 'undefined'){
6584                 c.style += ' width:' + config.width + 'px;';
6585                 this.totalWidth += config.width;
6586             } else {
6587                 this.totalWidth += 100; // assume minimum of 100 per column?
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     c.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 c.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608             
6609             header.cn.push(c)
6610         }
6611         
6612         return header;
6613     },
6614     
6615     renderBody : function()
6616     {
6617         var body = {
6618             tag: 'tbody',
6619             cn : [
6620                 {
6621                     tag: 'tr',
6622                     cn : [
6623                         {
6624                             tag : 'td',
6625                             colspan :  this.cm.getColumnCount()
6626                         }
6627                     ]
6628                 }
6629             ]
6630         };
6631         
6632         return body;
6633     },
6634     
6635     renderFooter : function()
6636     {
6637         var footer = {
6638             tag: 'tfoot',
6639             cn : [
6640                 {
6641                     tag: 'tr',
6642                     cn : [
6643                         {
6644                             tag : 'td',
6645                             colspan :  this.cm.getColumnCount()
6646                         }
6647                     ]
6648                 }
6649             ]
6650         };
6651         
6652         return footer;
6653     },
6654     
6655     
6656     
6657     onLoad : function()
6658     {
6659 //        Roo.log('ds onload');
6660         this.clear();
6661         
6662         var _this = this;
6663         var cm = this.cm;
6664         var ds = this.store;
6665         
6666         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668             if (_this.store.sortInfo) {
6669                     
6670                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6672                 }
6673                 
6674                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6676                 }
6677             }
6678         });
6679         
6680         var tbody =  this.mainBody;
6681               
6682         if(ds.getCount() > 0){
6683             ds.data.each(function(d,rowIndex){
6684                 var row =  this.renderRow(cm, ds, rowIndex);
6685                 
6686                 tbody.createChild(row);
6687                 
6688                 var _this = this;
6689                 
6690                 if(row.cellObjects.length){
6691                     Roo.each(row.cellObjects, function(r){
6692                         _this.renderCellObject(r);
6693                     })
6694                 }
6695                 
6696             }, this);
6697         }
6698         
6699         var tfoot = this.el.select('tfoot', true).first();
6700         
6701         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6702             
6703             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6704             
6705             var total = this.ds.getTotalCount();
6706             
6707             if(this.footer.pageSize < total){
6708                 this.mainFoot.show();
6709             }
6710         }
6711         
6712         Roo.each(this.el.select('tbody td', true).elements, function(e){
6713             e.on('mouseover', _this.onMouseover, _this);
6714         });
6715         
6716         Roo.each(this.el.select('tbody td', true).elements, function(e){
6717             e.on('mouseout', _this.onMouseout, _this);
6718         });
6719         this.fireEvent('rowsrendered', this);
6720         
6721         this.autoSize();
6722     },
6723     
6724     
6725     onUpdate : function(ds,record)
6726     {
6727         this.refreshRow(record);
6728         this.autoSize();
6729     },
6730     
6731     onRemove : function(ds, record, index, isUpdate){
6732         if(isUpdate !== true){
6733             this.fireEvent("beforerowremoved", this, index, record);
6734         }
6735         var bt = this.mainBody.dom;
6736         
6737         var rows = this.el.select('tbody > tr', true).elements;
6738         
6739         if(typeof(rows[index]) != 'undefined'){
6740             bt.removeChild(rows[index].dom);
6741         }
6742         
6743 //        if(bt.rows[index]){
6744 //            bt.removeChild(bt.rows[index]);
6745 //        }
6746         
6747         if(isUpdate !== true){
6748             //this.stripeRows(index);
6749             //this.syncRowHeights(index, index);
6750             //this.layout();
6751             this.fireEvent("rowremoved", this, index, record);
6752         }
6753     },
6754     
6755     onAdd : function(ds, records, rowIndex)
6756     {
6757         //Roo.log('on Add called');
6758         // - note this does not handle multiple adding very well..
6759         var bt = this.mainBody.dom;
6760         for (var i =0 ; i < records.length;i++) {
6761             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762             //Roo.log(records[i]);
6763             //Roo.log(this.store.getAt(rowIndex+i));
6764             this.insertRow(this.store, rowIndex + i, false);
6765             return;
6766         }
6767         
6768     },
6769     
6770     
6771     refreshRow : function(record){
6772         var ds = this.store, index;
6773         if(typeof record == 'number'){
6774             index = record;
6775             record = ds.getAt(index);
6776         }else{
6777             index = ds.indexOf(record);
6778         }
6779         this.insertRow(ds, index, true);
6780         this.autoSize();
6781         this.onRemove(ds, record, index+1, true);
6782         this.autoSize();
6783         //this.syncRowHeights(index, index);
6784         //this.layout();
6785         this.fireEvent("rowupdated", this, index, record);
6786     },
6787     
6788     insertRow : function(dm, rowIndex, isUpdate){
6789         
6790         if(!isUpdate){
6791             this.fireEvent("beforerowsinserted", this, rowIndex);
6792         }
6793             //var s = this.getScrollState();
6794         var row = this.renderRow(this.cm, this.store, rowIndex);
6795         // insert before rowIndex..
6796         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6797         
6798         var _this = this;
6799                 
6800         if(row.cellObjects.length){
6801             Roo.each(row.cellObjects, function(r){
6802                 _this.renderCellObject(r);
6803             })
6804         }
6805             
6806         if(!isUpdate){
6807             this.fireEvent("rowsinserted", this, rowIndex);
6808             //this.syncRowHeights(firstRow, lastRow);
6809             //this.stripeRows(firstRow);
6810             //this.layout();
6811         }
6812         
6813     },
6814     
6815     
6816     getRowDom : function(rowIndex)
6817     {
6818         var rows = this.el.select('tbody > tr', true).elements;
6819         
6820         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6821         
6822     },
6823     // returns the object tree for a tr..
6824   
6825     
6826     renderRow : function(cm, ds, rowIndex) 
6827     {
6828         var d = ds.getAt(rowIndex);
6829         
6830         var row = {
6831             tag : 'tr',
6832             cls : 'x-row-' + rowIndex,
6833             cn : []
6834         };
6835             
6836         var cellObjects = [];
6837         
6838         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839             var config = cm.config[i];
6840             
6841             var renderer = cm.getRenderer(i);
6842             var value = '';
6843             var id = false;
6844             
6845             if(typeof(renderer) !== 'undefined'){
6846                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6847             }
6848             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849             // and are rendered into the cells after the row is rendered - using the id for the element.
6850             
6851             if(typeof(value) === 'object'){
6852                 id = Roo.id();
6853                 cellObjects.push({
6854                     container : id,
6855                     cfg : value 
6856                 })
6857             }
6858             
6859             var rowcfg = {
6860                 record: d,
6861                 rowIndex : rowIndex,
6862                 colIndex : i,
6863                 rowClass : ''
6864             };
6865
6866             this.fireEvent('rowclass', this, rowcfg);
6867             
6868             var td = {
6869                 tag: 'td',
6870                 cls : rowcfg.rowClass + ' x-col-' + i,
6871                 style: '',
6872                 html: (typeof(value) === 'object') ? '' : value
6873             };
6874             
6875             if (id) {
6876                 td.id = id;
6877             }
6878             
6879             if(typeof(config.colspan) != 'undefined'){
6880                 td.colspan = config.colspan;
6881             }
6882             
6883             if(typeof(config.hidden) != 'undefined' && config.hidden){
6884                 td.style += ' display:none;';
6885             }
6886             
6887             if(typeof(config.align) != 'undefined' && config.align.length){
6888                 td.style += ' text-align:' + config.align + ';';
6889             }
6890             if(typeof(config.valign) != 'undefined' && config.valign.length){
6891                 td.style += ' vertical-align:' + config.valign + ';';
6892             }
6893             
6894             if(typeof(config.width) != 'undefined'){
6895                 td.style += ' width:' +  config.width + 'px;';
6896             }
6897             
6898             if(typeof(config.cursor) != 'undefined'){
6899                 td.style += ' cursor:' +  config.cursor + ';';
6900             }
6901             
6902             if(typeof(config.cls) != 'undefined'){
6903                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6904             }
6905             
6906             ['xs','sm','md','lg'].map(function(size){
6907                 
6908                 if(typeof(config[size]) == 'undefined'){
6909                     return;
6910                 }
6911                 
6912                 if (!config[size]) { // 0 = hidden
6913                     td.cls += ' hidden-' + size;
6914                     return;
6915                 }
6916                 
6917                 td.cls += ' col-' + size + '-' + config[size];
6918
6919             });
6920             
6921             row.cn.push(td);
6922            
6923         }
6924         
6925         row.cellObjects = cellObjects;
6926         
6927         return row;
6928           
6929     },
6930     
6931     
6932     
6933     onBeforeLoad : function()
6934     {
6935         
6936     },
6937      /**
6938      * Remove all rows
6939      */
6940     clear : function()
6941     {
6942         this.el.select('tbody', true).first().dom.innerHTML = '';
6943     },
6944     /**
6945      * Show or hide a row.
6946      * @param {Number} rowIndex to show or hide
6947      * @param {Boolean} state hide
6948      */
6949     setRowVisibility : function(rowIndex, state)
6950     {
6951         var bt = this.mainBody.dom;
6952         
6953         var rows = this.el.select('tbody > tr', true).elements;
6954         
6955         if(typeof(rows[rowIndex]) == 'undefined'){
6956             return;
6957         }
6958         rows[rowIndex].dom.style.display = state ? '' : 'none';
6959     },
6960     
6961     
6962     getSelectionModel : function(){
6963         if(!this.selModel){
6964             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6965         }
6966         return this.selModel;
6967     },
6968     /*
6969      * Render the Roo.bootstrap object from renderder
6970      */
6971     renderCellObject : function(r)
6972     {
6973         var _this = this;
6974         
6975         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6976         
6977         var t = r.cfg.render(r.container);
6978         
6979         if(r.cfg.cn){
6980             Roo.each(r.cfg.cn, function(c){
6981                 var child = {
6982                     container: t.getChildContainer(),
6983                     cfg: c
6984                 };
6985                 _this.renderCellObject(child);
6986             })
6987         }
6988     },
6989     
6990     getRowIndex : function(row)
6991     {
6992         var rowIndex = -1;
6993         
6994         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6995             if(el != row){
6996                 return;
6997             }
6998             
6999             rowIndex = index;
7000         });
7001         
7002         return rowIndex;
7003     },
7004      /**
7005      * Returns the grid's underlying element = used by panel.Grid
7006      * @return {Element} The element
7007      */
7008     getGridEl : function(){
7009         return this.el;
7010     },
7011      /**
7012      * Forces a resize - used by panel.Grid
7013      * @return {Element} The element
7014      */
7015     autoSize : function()
7016     {
7017         //var ctr = Roo.get(this.container.dom.parentElement);
7018         var ctr = Roo.get(this.el.dom);
7019         
7020         var thd = this.getGridEl().select('thead',true).first();
7021         var tbd = this.getGridEl().select('tbody', true).first();
7022         var tfd = this.getGridEl().select('tfoot', true).first();
7023         
7024         var cw = ctr.getWidth();
7025         
7026         if (tbd) {
7027             
7028             tbd.setSize(ctr.getWidth(),
7029                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7030             );
7031             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7032             cw -= barsize;
7033         }
7034         cw = Math.max(cw, this.totalWidth);
7035         this.getGridEl().select('tr',true).setWidth(cw);
7036         // resize 'expandable coloumn?
7037         
7038         return; // we doe not have a view in this design..
7039         
7040     },
7041     onBodyScroll: function()
7042     {
7043         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7044         if(this.mainHead){
7045             this.mainHead.setStyle({
7046                 'position' : 'relative',
7047                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7048             });
7049         }
7050         
7051         if(this.lazyLoad){
7052             
7053             var scrollHeight = this.mainBody.dom.scrollHeight;
7054             
7055             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7056             
7057             var height = this.mainBody.getHeight();
7058             
7059             if(scrollHeight - height == scrollTop) {
7060                 
7061                 var total = this.ds.getTotalCount();
7062                 
7063                 if(this.footer.cursor + this.footer.pageSize < total){
7064                     
7065                     this.footer.ds.load({
7066                         params : {
7067                             start : this.footer.cursor + this.footer.pageSize,
7068                             limit : this.footer.pageSize
7069                         },
7070                         add : true
7071                     });
7072                 }
7073             }
7074             
7075         }
7076     },
7077     
7078     onHeaderChange : function()
7079     {
7080         var header = this.renderHeader();
7081         var table = this.el.select('table', true).first();
7082         
7083         this.mainHead.remove();
7084         this.mainHead = table.createChild(header, this.mainBody, false);
7085     },
7086     
7087     onHiddenChange : function(colModel, colIndex, hidden)
7088     {
7089         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7091         
7092         this.CSS.updateRule(thSelector, "display", "");
7093         this.CSS.updateRule(tdSelector, "display", "");
7094         
7095         if(hidden){
7096             this.CSS.updateRule(thSelector, "display", "none");
7097             this.CSS.updateRule(tdSelector, "display", "none");
7098         }
7099         
7100         this.onHeaderChange();
7101         this.onLoad();
7102     },
7103     
7104     setColumnWidth: function(col_index, width)
7105     {
7106         // width = "md-2 xs-2..."
7107         if(!this.colModel.config[col_index]) {
7108             return;
7109         }
7110         
7111         var w = width.split(" ");
7112         
7113         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7114         
7115         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7116         
7117         
7118         for(var j = 0; j < w.length; j++) {
7119             
7120             if(!w[j]) {
7121                 continue;
7122             }
7123             
7124             var size_cls = w[j].split("-");
7125             
7126             if(!Number.isInteger(size_cls[1] * 1)) {
7127                 continue;
7128             }
7129             
7130             if(!this.colModel.config[col_index][size_cls[0]]) {
7131                 continue;
7132             }
7133             
7134             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7135                 continue;
7136             }
7137             
7138             h_row[0].classList.replace(
7139                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7140                 "col-"+size_cls[0]+"-"+size_cls[1]
7141             );
7142             
7143             for(var i = 0; i < rows.length; i++) {
7144                 
7145                 var size_cls = w[j].split("-");
7146                 
7147                 if(!Number.isInteger(size_cls[1] * 1)) {
7148                     continue;
7149                 }
7150                 
7151                 if(!this.colModel.config[col_index][size_cls[0]]) {
7152                     continue;
7153                 }
7154                 
7155                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7156                     continue;
7157                 }
7158                 
7159                 rows[i].classList.replace(
7160                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7161                     "col-"+size_cls[0]+"-"+size_cls[1]
7162                 );
7163             }
7164             
7165             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7166         }
7167     }
7168 });
7169
7170  
7171
7172  /*
7173  * - LGPL
7174  *
7175  * table cell
7176  * 
7177  */
7178
7179 /**
7180  * @class Roo.bootstrap.TableCell
7181  * @extends Roo.bootstrap.Component
7182  * Bootstrap TableCell class
7183  * @cfg {String} html cell contain text
7184  * @cfg {String} cls cell class
7185  * @cfg {String} tag cell tag (td|th) default td
7186  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7187  * @cfg {String} align Aligns the content in a cell
7188  * @cfg {String} axis Categorizes cells
7189  * @cfg {String} bgcolor Specifies the background color of a cell
7190  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7191  * @cfg {Number} colspan Specifies the number of columns a cell should span
7192  * @cfg {String} headers Specifies one or more header cells a cell is related to
7193  * @cfg {Number} height Sets the height of a cell
7194  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7195  * @cfg {Number} rowspan Sets the number of rows a cell should span
7196  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7197  * @cfg {String} valign Vertical aligns the content in a cell
7198  * @cfg {Number} width Specifies the width of a cell
7199  * 
7200  * @constructor
7201  * Create a new TableCell
7202  * @param {Object} config The config object
7203  */
7204
7205 Roo.bootstrap.TableCell = function(config){
7206     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7207 };
7208
7209 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7210     
7211     html: false,
7212     cls: false,
7213     tag: false,
7214     abbr: false,
7215     align: false,
7216     axis: false,
7217     bgcolor: false,
7218     charoff: false,
7219     colspan: false,
7220     headers: false,
7221     height: false,
7222     nowrap: false,
7223     rowspan: false,
7224     scope: false,
7225     valign: false,
7226     width: false,
7227     
7228     
7229     getAutoCreate : function(){
7230         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7231         
7232         cfg = {
7233             tag: 'td'
7234         };
7235         
7236         if(this.tag){
7237             cfg.tag = this.tag;
7238         }
7239         
7240         if (this.html) {
7241             cfg.html=this.html
7242         }
7243         if (this.cls) {
7244             cfg.cls=this.cls
7245         }
7246         if (this.abbr) {
7247             cfg.abbr=this.abbr
7248         }
7249         if (this.align) {
7250             cfg.align=this.align
7251         }
7252         if (this.axis) {
7253             cfg.axis=this.axis
7254         }
7255         if (this.bgcolor) {
7256             cfg.bgcolor=this.bgcolor
7257         }
7258         if (this.charoff) {
7259             cfg.charoff=this.charoff
7260         }
7261         if (this.colspan) {
7262             cfg.colspan=this.colspan
7263         }
7264         if (this.headers) {
7265             cfg.headers=this.headers
7266         }
7267         if (this.height) {
7268             cfg.height=this.height
7269         }
7270         if (this.nowrap) {
7271             cfg.nowrap=this.nowrap
7272         }
7273         if (this.rowspan) {
7274             cfg.rowspan=this.rowspan
7275         }
7276         if (this.scope) {
7277             cfg.scope=this.scope
7278         }
7279         if (this.valign) {
7280             cfg.valign=this.valign
7281         }
7282         if (this.width) {
7283             cfg.width=this.width
7284         }
7285         
7286         
7287         return cfg;
7288     }
7289    
7290 });
7291
7292  
7293
7294  /*
7295  * - LGPL
7296  *
7297  * table row
7298  * 
7299  */
7300
7301 /**
7302  * @class Roo.bootstrap.TableRow
7303  * @extends Roo.bootstrap.Component
7304  * Bootstrap TableRow class
7305  * @cfg {String} cls row class
7306  * @cfg {String} align Aligns the content in a table row
7307  * @cfg {String} bgcolor Specifies a background color for a table row
7308  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7309  * @cfg {String} valign Vertical aligns the content in a table row
7310  * 
7311  * @constructor
7312  * Create a new TableRow
7313  * @param {Object} config The config object
7314  */
7315
7316 Roo.bootstrap.TableRow = function(config){
7317     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7318 };
7319
7320 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7321     
7322     cls: false,
7323     align: false,
7324     bgcolor: false,
7325     charoff: false,
7326     valign: false,
7327     
7328     getAutoCreate : function(){
7329         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7330         
7331         cfg = {
7332             tag: 'tr'
7333         };
7334             
7335         if(this.cls){
7336             cfg.cls = this.cls;
7337         }
7338         if(this.align){
7339             cfg.align = this.align;
7340         }
7341         if(this.bgcolor){
7342             cfg.bgcolor = this.bgcolor;
7343         }
7344         if(this.charoff){
7345             cfg.charoff = this.charoff;
7346         }
7347         if(this.valign){
7348             cfg.valign = this.valign;
7349         }
7350         
7351         return cfg;
7352     }
7353    
7354 });
7355
7356  
7357
7358  /*
7359  * - LGPL
7360  *
7361  * table body
7362  * 
7363  */
7364
7365 /**
7366  * @class Roo.bootstrap.TableBody
7367  * @extends Roo.bootstrap.Component
7368  * Bootstrap TableBody class
7369  * @cfg {String} cls element class
7370  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7371  * @cfg {String} align Aligns the content inside the element
7372  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7373  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7374  * 
7375  * @constructor
7376  * Create a new TableBody
7377  * @param {Object} config The config object
7378  */
7379
7380 Roo.bootstrap.TableBody = function(config){
7381     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7382 };
7383
7384 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7385     
7386     cls: false,
7387     tag: false,
7388     align: false,
7389     charoff: false,
7390     valign: false,
7391     
7392     getAutoCreate : function(){
7393         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7394         
7395         cfg = {
7396             tag: 'tbody'
7397         };
7398             
7399         if (this.cls) {
7400             cfg.cls=this.cls
7401         }
7402         if(this.tag){
7403             cfg.tag = this.tag;
7404         }
7405         
7406         if(this.align){
7407             cfg.align = this.align;
7408         }
7409         if(this.charoff){
7410             cfg.charoff = this.charoff;
7411         }
7412         if(this.valign){
7413             cfg.valign = this.valign;
7414         }
7415         
7416         return cfg;
7417     }
7418     
7419     
7420 //    initEvents : function()
7421 //    {
7422 //        
7423 //        if(!this.store){
7424 //            return;
7425 //        }
7426 //        
7427 //        this.store = Roo.factory(this.store, Roo.data);
7428 //        this.store.on('load', this.onLoad, this);
7429 //        
7430 //        this.store.load();
7431 //        
7432 //    },
7433 //    
7434 //    onLoad: function () 
7435 //    {   
7436 //        this.fireEvent('load', this);
7437 //    }
7438 //    
7439 //   
7440 });
7441
7442  
7443
7444  /*
7445  * Based on:
7446  * Ext JS Library 1.1.1
7447  * Copyright(c) 2006-2007, Ext JS, LLC.
7448  *
7449  * Originally Released Under LGPL - original licence link has changed is not relivant.
7450  *
7451  * Fork - LGPL
7452  * <script type="text/javascript">
7453  */
7454
7455 // as we use this in bootstrap.
7456 Roo.namespace('Roo.form');
7457  /**
7458  * @class Roo.form.Action
7459  * Internal Class used to handle form actions
7460  * @constructor
7461  * @param {Roo.form.BasicForm} el The form element or its id
7462  * @param {Object} config Configuration options
7463  */
7464
7465  
7466  
7467 // define the action interface
7468 Roo.form.Action = function(form, options){
7469     this.form = form;
7470     this.options = options || {};
7471 };
7472 /**
7473  * Client Validation Failed
7474  * @const 
7475  */
7476 Roo.form.Action.CLIENT_INVALID = 'client';
7477 /**
7478  * Server Validation Failed
7479  * @const 
7480  */
7481 Roo.form.Action.SERVER_INVALID = 'server';
7482  /**
7483  * Connect to Server Failed
7484  * @const 
7485  */
7486 Roo.form.Action.CONNECT_FAILURE = 'connect';
7487 /**
7488  * Reading Data from Server Failed
7489  * @const 
7490  */
7491 Roo.form.Action.LOAD_FAILURE = 'load';
7492
7493 Roo.form.Action.prototype = {
7494     type : 'default',
7495     failureType : undefined,
7496     response : undefined,
7497     result : undefined,
7498
7499     // interface method
7500     run : function(options){
7501
7502     },
7503
7504     // interface method
7505     success : function(response){
7506
7507     },
7508
7509     // interface method
7510     handleResponse : function(response){
7511
7512     },
7513
7514     // default connection failure
7515     failure : function(response){
7516         
7517         this.response = response;
7518         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7519         this.form.afterAction(this, false);
7520     },
7521
7522     processResponse : function(response){
7523         this.response = response;
7524         if(!response.responseText){
7525             return true;
7526         }
7527         this.result = this.handleResponse(response);
7528         return this.result;
7529     },
7530
7531     // utility functions used internally
7532     getUrl : function(appendParams){
7533         var url = this.options.url || this.form.url || this.form.el.dom.action;
7534         if(appendParams){
7535             var p = this.getParams();
7536             if(p){
7537                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7538             }
7539         }
7540         return url;
7541     },
7542
7543     getMethod : function(){
7544         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7545     },
7546
7547     getParams : function(){
7548         var bp = this.form.baseParams;
7549         var p = this.options.params;
7550         if(p){
7551             if(typeof p == "object"){
7552                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7553             }else if(typeof p == 'string' && bp){
7554                 p += '&' + Roo.urlEncode(bp);
7555             }
7556         }else if(bp){
7557             p = Roo.urlEncode(bp);
7558         }
7559         return p;
7560     },
7561
7562     createCallback : function(){
7563         return {
7564             success: this.success,
7565             failure: this.failure,
7566             scope: this,
7567             timeout: (this.form.timeout*1000),
7568             upload: this.form.fileUpload ? this.success : undefined
7569         };
7570     }
7571 };
7572
7573 Roo.form.Action.Submit = function(form, options){
7574     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7575 };
7576
7577 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7578     type : 'submit',
7579
7580     haveProgress : false,
7581     uploadComplete : false,
7582     
7583     // uploadProgress indicator.
7584     uploadProgress : function()
7585     {
7586         if (!this.form.progressUrl) {
7587             return;
7588         }
7589         
7590         if (!this.haveProgress) {
7591             Roo.MessageBox.progress("Uploading", "Uploading");
7592         }
7593         if (this.uploadComplete) {
7594            Roo.MessageBox.hide();
7595            return;
7596         }
7597         
7598         this.haveProgress = true;
7599    
7600         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7601         
7602         var c = new Roo.data.Connection();
7603         c.request({
7604             url : this.form.progressUrl,
7605             params: {
7606                 id : uid
7607             },
7608             method: 'GET',
7609             success : function(req){
7610                //console.log(data);
7611                 var rdata = false;
7612                 var edata;
7613                 try  {
7614                    rdata = Roo.decode(req.responseText)
7615                 } catch (e) {
7616                     Roo.log("Invalid data from server..");
7617                     Roo.log(edata);
7618                     return;
7619                 }
7620                 if (!rdata || !rdata.success) {
7621                     Roo.log(rdata);
7622                     Roo.MessageBox.alert(Roo.encode(rdata));
7623                     return;
7624                 }
7625                 var data = rdata.data;
7626                 
7627                 if (this.uploadComplete) {
7628                    Roo.MessageBox.hide();
7629                    return;
7630                 }
7631                    
7632                 if (data){
7633                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7634                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7635                     );
7636                 }
7637                 this.uploadProgress.defer(2000,this);
7638             },
7639        
7640             failure: function(data) {
7641                 Roo.log('progress url failed ');
7642                 Roo.log(data);
7643             },
7644             scope : this
7645         });
7646            
7647     },
7648     
7649     
7650     run : function()
7651     {
7652         // run get Values on the form, so it syncs any secondary forms.
7653         this.form.getValues();
7654         
7655         var o = this.options;
7656         var method = this.getMethod();
7657         var isPost = method == 'POST';
7658         if(o.clientValidation === false || this.form.isValid()){
7659             
7660             if (this.form.progressUrl) {
7661                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7662                     (new Date() * 1) + '' + Math.random());
7663                     
7664             } 
7665             
7666             
7667             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7668                 form:this.form.el.dom,
7669                 url:this.getUrl(!isPost),
7670                 method: method,
7671                 params:isPost ? this.getParams() : null,
7672                 isUpload: this.form.fileUpload
7673             }));
7674             
7675             this.uploadProgress();
7676
7677         }else if (o.clientValidation !== false){ // client validation failed
7678             this.failureType = Roo.form.Action.CLIENT_INVALID;
7679             this.form.afterAction(this, false);
7680         }
7681     },
7682
7683     success : function(response)
7684     {
7685         this.uploadComplete= true;
7686         if (this.haveProgress) {
7687             Roo.MessageBox.hide();
7688         }
7689         
7690         
7691         var result = this.processResponse(response);
7692         if(result === true || result.success){
7693             this.form.afterAction(this, true);
7694             return;
7695         }
7696         if(result.errors){
7697             this.form.markInvalid(result.errors);
7698             this.failureType = Roo.form.Action.SERVER_INVALID;
7699         }
7700         this.form.afterAction(this, false);
7701     },
7702     failure : function(response)
7703     {
7704         this.uploadComplete= true;
7705         if (this.haveProgress) {
7706             Roo.MessageBox.hide();
7707         }
7708         
7709         this.response = response;
7710         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7711         this.form.afterAction(this, false);
7712     },
7713     
7714     handleResponse : function(response){
7715         if(this.form.errorReader){
7716             var rs = this.form.errorReader.read(response);
7717             var errors = [];
7718             if(rs.records){
7719                 for(var i = 0, len = rs.records.length; i < len; i++) {
7720                     var r = rs.records[i];
7721                     errors[i] = r.data;
7722                 }
7723             }
7724             if(errors.length < 1){
7725                 errors = null;
7726             }
7727             return {
7728                 success : rs.success,
7729                 errors : errors
7730             };
7731         }
7732         var ret = false;
7733         try {
7734             ret = Roo.decode(response.responseText);
7735         } catch (e) {
7736             ret = {
7737                 success: false,
7738                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7739                 errors : []
7740             };
7741         }
7742         return ret;
7743         
7744     }
7745 });
7746
7747
7748 Roo.form.Action.Load = function(form, options){
7749     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7750     this.reader = this.form.reader;
7751 };
7752
7753 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7754     type : 'load',
7755
7756     run : function(){
7757         
7758         Roo.Ajax.request(Roo.apply(
7759                 this.createCallback(), {
7760                     method:this.getMethod(),
7761                     url:this.getUrl(false),
7762                     params:this.getParams()
7763         }));
7764     },
7765
7766     success : function(response){
7767         
7768         var result = this.processResponse(response);
7769         if(result === true || !result.success || !result.data){
7770             this.failureType = Roo.form.Action.LOAD_FAILURE;
7771             this.form.afterAction(this, false);
7772             return;
7773         }
7774         this.form.clearInvalid();
7775         this.form.setValues(result.data);
7776         this.form.afterAction(this, true);
7777     },
7778
7779     handleResponse : function(response){
7780         if(this.form.reader){
7781             var rs = this.form.reader.read(response);
7782             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7783             return {
7784                 success : rs.success,
7785                 data : data
7786             };
7787         }
7788         return Roo.decode(response.responseText);
7789     }
7790 });
7791
7792 Roo.form.Action.ACTION_TYPES = {
7793     'load' : Roo.form.Action.Load,
7794     'submit' : Roo.form.Action.Submit
7795 };/*
7796  * - LGPL
7797  *
7798  * form
7799  *
7800  */
7801
7802 /**
7803  * @class Roo.bootstrap.Form
7804  * @extends Roo.bootstrap.Component
7805  * Bootstrap Form class
7806  * @cfg {String} method  GET | POST (default POST)
7807  * @cfg {String} labelAlign top | left (default top)
7808  * @cfg {String} align left  | right - for navbars
7809  * @cfg {Boolean} loadMask load mask when submit (default true)
7810
7811  *
7812  * @constructor
7813  * Create a new Form
7814  * @param {Object} config The config object
7815  */
7816
7817
7818 Roo.bootstrap.Form = function(config){
7819     
7820     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7821     
7822     Roo.bootstrap.Form.popover.apply();
7823     
7824     this.addEvents({
7825         /**
7826          * @event clientvalidation
7827          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7828          * @param {Form} this
7829          * @param {Boolean} valid true if the form has passed client-side validation
7830          */
7831         clientvalidation: true,
7832         /**
7833          * @event beforeaction
7834          * Fires before any action is performed. Return false to cancel the action.
7835          * @param {Form} this
7836          * @param {Action} action The action to be performed
7837          */
7838         beforeaction: true,
7839         /**
7840          * @event actionfailed
7841          * Fires when an action fails.
7842          * @param {Form} this
7843          * @param {Action} action The action that failed
7844          */
7845         actionfailed : true,
7846         /**
7847          * @event actioncomplete
7848          * Fires when an action is completed.
7849          * @param {Form} this
7850          * @param {Action} action The action that completed
7851          */
7852         actioncomplete : true
7853     });
7854 };
7855
7856 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7857
7858      /**
7859      * @cfg {String} method
7860      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7861      */
7862     method : 'POST',
7863     /**
7864      * @cfg {String} url
7865      * The URL to use for form actions if one isn't supplied in the action options.
7866      */
7867     /**
7868      * @cfg {Boolean} fileUpload
7869      * Set to true if this form is a file upload.
7870      */
7871
7872     /**
7873      * @cfg {Object} baseParams
7874      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7875      */
7876
7877     /**
7878      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7879      */
7880     timeout: 30,
7881     /**
7882      * @cfg {Sting} align (left|right) for navbar forms
7883      */
7884     align : 'left',
7885
7886     // private
7887     activeAction : null,
7888
7889     /**
7890      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7891      * element by passing it or its id or mask the form itself by passing in true.
7892      * @type Mixed
7893      */
7894     waitMsgTarget : false,
7895
7896     loadMask : true,
7897     
7898     /**
7899      * @cfg {Boolean} errorMask (true|false) default false
7900      */
7901     errorMask : false,
7902     
7903     /**
7904      * @cfg {Number} maskOffset Default 100
7905      */
7906     maskOffset : 100,
7907     
7908     /**
7909      * @cfg {Boolean} maskBody
7910      */
7911     maskBody : false,
7912
7913     getAutoCreate : function(){
7914
7915         var cfg = {
7916             tag: 'form',
7917             method : this.method || 'POST',
7918             id : this.id || Roo.id(),
7919             cls : ''
7920         };
7921         if (this.parent().xtype.match(/^Nav/)) {
7922             cfg.cls = 'navbar-form navbar-' + this.align;
7923
7924         }
7925
7926         if (this.labelAlign == 'left' ) {
7927             cfg.cls += ' form-horizontal';
7928         }
7929
7930
7931         return cfg;
7932     },
7933     initEvents : function()
7934     {
7935         this.el.on('submit', this.onSubmit, this);
7936         // this was added as random key presses on the form where triggering form submit.
7937         this.el.on('keypress', function(e) {
7938             if (e.getCharCode() != 13) {
7939                 return true;
7940             }
7941             // we might need to allow it for textareas.. and some other items.
7942             // check e.getTarget().
7943
7944             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7945                 return true;
7946             }
7947
7948             Roo.log("keypress blocked");
7949
7950             e.preventDefault();
7951             return false;
7952         });
7953         
7954     },
7955     // private
7956     onSubmit : function(e){
7957         e.stopEvent();
7958     },
7959
7960      /**
7961      * Returns true if client-side validation on the form is successful.
7962      * @return Boolean
7963      */
7964     isValid : function(){
7965         var items = this.getItems();
7966         var valid = true;
7967         var target = false;
7968         
7969         items.each(function(f){
7970             
7971             if(f.validate()){
7972                 return;
7973             }
7974             
7975             Roo.log('invalid field: ' + f.name);
7976             
7977             valid = false;
7978
7979             if(!target && f.el.isVisible(true)){
7980                 target = f;
7981             }
7982            
7983         });
7984         
7985         if(this.errorMask && !valid){
7986             Roo.bootstrap.Form.popover.mask(this, target);
7987         }
7988         
7989         return valid;
7990     },
7991     
7992     /**
7993      * Returns true if any fields in this form have changed since their original load.
7994      * @return Boolean
7995      */
7996     isDirty : function(){
7997         var dirty = false;
7998         var items = this.getItems();
7999         items.each(function(f){
8000            if(f.isDirty()){
8001                dirty = true;
8002                return false;
8003            }
8004            return true;
8005         });
8006         return dirty;
8007     },
8008      /**
8009      * Performs a predefined action (submit or load) or custom actions you define on this form.
8010      * @param {String} actionName The name of the action type
8011      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8012      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8013      * accept other config options):
8014      * <pre>
8015 Property          Type             Description
8016 ----------------  ---------------  ----------------------------------------------------------------------------------
8017 url               String           The url for the action (defaults to the form's url)
8018 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8019 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8020 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8021                                    validate the form on the client (defaults to false)
8022      * </pre>
8023      * @return {BasicForm} this
8024      */
8025     doAction : function(action, options){
8026         if(typeof action == 'string'){
8027             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8028         }
8029         if(this.fireEvent('beforeaction', this, action) !== false){
8030             this.beforeAction(action);
8031             action.run.defer(100, action);
8032         }
8033         return this;
8034     },
8035
8036     // private
8037     beforeAction : function(action){
8038         var o = action.options;
8039         
8040         if(this.loadMask){
8041             
8042             if(this.maskBody){
8043                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8044             } else {
8045                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8046             }
8047         }
8048         // not really supported yet.. ??
8049
8050         //if(this.waitMsgTarget === true){
8051         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8052         //}else if(this.waitMsgTarget){
8053         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8054         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8055         //}else {
8056         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8057        // }
8058
8059     },
8060
8061     // private
8062     afterAction : function(action, success){
8063         this.activeAction = null;
8064         var o = action.options;
8065
8066         if(this.loadMask){
8067             
8068             if(this.maskBody){
8069                 Roo.get(document.body).unmask();
8070             } else {
8071                 this.el.unmask();
8072             }
8073         }
8074         
8075         //if(this.waitMsgTarget === true){
8076 //            this.el.unmask();
8077         //}else if(this.waitMsgTarget){
8078         //    this.waitMsgTarget.unmask();
8079         //}else{
8080         //    Roo.MessageBox.updateProgress(1);
8081         //    Roo.MessageBox.hide();
8082        // }
8083         //
8084         if(success){
8085             if(o.reset){
8086                 this.reset();
8087             }
8088             Roo.callback(o.success, o.scope, [this, action]);
8089             this.fireEvent('actioncomplete', this, action);
8090
8091         }else{
8092
8093             // failure condition..
8094             // we have a scenario where updates need confirming.
8095             // eg. if a locking scenario exists..
8096             // we look for { errors : { needs_confirm : true }} in the response.
8097             if (
8098                 (typeof(action.result) != 'undefined')  &&
8099                 (typeof(action.result.errors) != 'undefined')  &&
8100                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8101            ){
8102                 var _t = this;
8103                 Roo.log("not supported yet");
8104                  /*
8105
8106                 Roo.MessageBox.confirm(
8107                     "Change requires confirmation",
8108                     action.result.errorMsg,
8109                     function(r) {
8110                         if (r != 'yes') {
8111                             return;
8112                         }
8113                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8114                     }
8115
8116                 );
8117                 */
8118
8119
8120                 return;
8121             }
8122
8123             Roo.callback(o.failure, o.scope, [this, action]);
8124             // show an error message if no failed handler is set..
8125             if (!this.hasListener('actionfailed')) {
8126                 Roo.log("need to add dialog support");
8127                 /*
8128                 Roo.MessageBox.alert("Error",
8129                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8130                         action.result.errorMsg :
8131                         "Saving Failed, please check your entries or try again"
8132                 );
8133                 */
8134             }
8135
8136             this.fireEvent('actionfailed', this, action);
8137         }
8138
8139     },
8140     /**
8141      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8142      * @param {String} id The value to search for
8143      * @return Field
8144      */
8145     findField : function(id){
8146         var items = this.getItems();
8147         var field = items.get(id);
8148         if(!field){
8149              items.each(function(f){
8150                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8151                     field = f;
8152                     return false;
8153                 }
8154                 return true;
8155             });
8156         }
8157         return field || null;
8158     },
8159      /**
8160      * Mark fields in this form invalid in bulk.
8161      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8162      * @return {BasicForm} this
8163      */
8164     markInvalid : function(errors){
8165         if(errors instanceof Array){
8166             for(var i = 0, len = errors.length; i < len; i++){
8167                 var fieldError = errors[i];
8168                 var f = this.findField(fieldError.id);
8169                 if(f){
8170                     f.markInvalid(fieldError.msg);
8171                 }
8172             }
8173         }else{
8174             var field, id;
8175             for(id in errors){
8176                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8177                     field.markInvalid(errors[id]);
8178                 }
8179             }
8180         }
8181         //Roo.each(this.childForms || [], function (f) {
8182         //    f.markInvalid(errors);
8183         //});
8184
8185         return this;
8186     },
8187
8188     /**
8189      * Set values for fields in this form in bulk.
8190      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8191      * @return {BasicForm} this
8192      */
8193     setValues : function(values){
8194         if(values instanceof Array){ // array of objects
8195             for(var i = 0, len = values.length; i < len; i++){
8196                 var v = values[i];
8197                 var f = this.findField(v.id);
8198                 if(f){
8199                     f.setValue(v.value);
8200                     if(this.trackResetOnLoad){
8201                         f.originalValue = f.getValue();
8202                     }
8203                 }
8204             }
8205         }else{ // object hash
8206             var field, id;
8207             for(id in values){
8208                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8209
8210                     if (field.setFromData &&
8211                         field.valueField &&
8212                         field.displayField &&
8213                         // combos' with local stores can
8214                         // be queried via setValue()
8215                         // to set their value..
8216                         (field.store && !field.store.isLocal)
8217                         ) {
8218                         // it's a combo
8219                         var sd = { };
8220                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8221                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8222                         field.setFromData(sd);
8223
8224                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8225                         
8226                         field.setFromData(values);
8227                         
8228                     } else {
8229                         field.setValue(values[id]);
8230                     }
8231
8232
8233                     if(this.trackResetOnLoad){
8234                         field.originalValue = field.getValue();
8235                     }
8236                 }
8237             }
8238         }
8239
8240         //Roo.each(this.childForms || [], function (f) {
8241         //    f.setValues(values);
8242         //});
8243
8244         return this;
8245     },
8246
8247     /**
8248      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8249      * they are returned as an array.
8250      * @param {Boolean} asString
8251      * @return {Object}
8252      */
8253     getValues : function(asString){
8254         //if (this.childForms) {
8255             // copy values from the child forms
8256         //    Roo.each(this.childForms, function (f) {
8257         //        this.setValues(f.getValues());
8258         //    }, this);
8259         //}
8260
8261
8262
8263         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8264         if(asString === true){
8265             return fs;
8266         }
8267         return Roo.urlDecode(fs);
8268     },
8269
8270     /**
8271      * Returns the fields in this form as an object with key/value pairs.
8272      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8273      * @return {Object}
8274      */
8275     getFieldValues : function(with_hidden)
8276     {
8277         var items = this.getItems();
8278         var ret = {};
8279         items.each(function(f){
8280             
8281             if (!f.getName()) {
8282                 return;
8283             }
8284             
8285             var v = f.getValue();
8286             
8287             if (f.inputType =='radio') {
8288                 if (typeof(ret[f.getName()]) == 'undefined') {
8289                     ret[f.getName()] = ''; // empty..
8290                 }
8291
8292                 if (!f.el.dom.checked) {
8293                     return;
8294
8295                 }
8296                 v = f.el.dom.value;
8297
8298             }
8299             
8300             if(f.xtype == 'MoneyField'){
8301                 ret[f.currencyName] = f.getCurrency();
8302             }
8303
8304             // not sure if this supported any more..
8305             if ((typeof(v) == 'object') && f.getRawValue) {
8306                 v = f.getRawValue() ; // dates..
8307             }
8308             // combo boxes where name != hiddenName...
8309             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8310                 ret[f.name] = f.getRawValue();
8311             }
8312             ret[f.getName()] = v;
8313         });
8314
8315         return ret;
8316     },
8317
8318     /**
8319      * Clears all invalid messages in this form.
8320      * @return {BasicForm} this
8321      */
8322     clearInvalid : function(){
8323         var items = this.getItems();
8324
8325         items.each(function(f){
8326            f.clearInvalid();
8327         });
8328
8329         return this;
8330     },
8331
8332     /**
8333      * Resets this form.
8334      * @return {BasicForm} this
8335      */
8336     reset : function(){
8337         var items = this.getItems();
8338         items.each(function(f){
8339             f.reset();
8340         });
8341
8342         Roo.each(this.childForms || [], function (f) {
8343             f.reset();
8344         });
8345
8346
8347         return this;
8348     },
8349     
8350     getItems : function()
8351     {
8352         var r=new Roo.util.MixedCollection(false, function(o){
8353             return o.id || (o.id = Roo.id());
8354         });
8355         var iter = function(el) {
8356             if (el.inputEl) {
8357                 r.add(el);
8358             }
8359             if (!el.items) {
8360                 return;
8361             }
8362             Roo.each(el.items,function(e) {
8363                 iter(e);
8364             });
8365         };
8366
8367         iter(this);
8368         return r;
8369     },
8370     
8371     hideFields : function(items)
8372     {
8373         Roo.each(items, function(i){
8374             
8375             var f = this.findField(i);
8376             
8377             if(!f){
8378                 return;
8379             }
8380             
8381             f.hide();
8382             
8383         }, this);
8384     },
8385     
8386     showFields : function(items)
8387     {
8388         Roo.each(items, function(i){
8389             
8390             var f = this.findField(i);
8391             
8392             if(!f){
8393                 return;
8394             }
8395             
8396             f.show();
8397             
8398         }, this);
8399     }
8400
8401 });
8402
8403 Roo.apply(Roo.bootstrap.Form, {
8404     
8405     popover : {
8406         
8407         padding : 5,
8408         
8409         isApplied : false,
8410         
8411         isMasked : false,
8412         
8413         form : false,
8414         
8415         target : false,
8416         
8417         toolTip : false,
8418         
8419         intervalID : false,
8420         
8421         maskEl : false,
8422         
8423         apply : function()
8424         {
8425             if(this.isApplied){
8426                 return;
8427             }
8428             
8429             this.maskEl = {
8430                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8431                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8432                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8433                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8434             };
8435             
8436             this.maskEl.top.enableDisplayMode("block");
8437             this.maskEl.left.enableDisplayMode("block");
8438             this.maskEl.bottom.enableDisplayMode("block");
8439             this.maskEl.right.enableDisplayMode("block");
8440             
8441             this.toolTip = new Roo.bootstrap.Tooltip({
8442                 cls : 'roo-form-error-popover',
8443                 alignment : {
8444                     'left' : ['r-l', [-2,0], 'right'],
8445                     'right' : ['l-r', [2,0], 'left'],
8446                     'bottom' : ['tl-bl', [0,2], 'top'],
8447                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8448                 }
8449             });
8450             
8451             this.toolTip.render(Roo.get(document.body));
8452
8453             this.toolTip.el.enableDisplayMode("block");
8454             
8455             Roo.get(document.body).on('click', function(){
8456                 this.unmask();
8457             }, this);
8458             
8459             Roo.get(document.body).on('touchstart', function(){
8460                 this.unmask();
8461             }, this);
8462             
8463             this.isApplied = true
8464         },
8465         
8466         mask : function(form, target)
8467         {
8468             this.form = form;
8469             
8470             this.target = target;
8471             
8472             if(!this.form.errorMask || !target.el){
8473                 return;
8474             }
8475             
8476             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8477             
8478             Roo.log(scrollable);
8479             
8480             var ot = this.target.el.calcOffsetsTo(scrollable);
8481             
8482             var scrollTo = ot[1] - this.form.maskOffset;
8483             
8484             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8485             
8486             scrollable.scrollTo('top', scrollTo);
8487             
8488             var box = this.target.el.getBox();
8489             Roo.log(box);
8490             var zIndex = Roo.bootstrap.Modal.zIndex++;
8491
8492             
8493             this.maskEl.top.setStyle('position', 'absolute');
8494             this.maskEl.top.setStyle('z-index', zIndex);
8495             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8496             this.maskEl.top.setLeft(0);
8497             this.maskEl.top.setTop(0);
8498             this.maskEl.top.show();
8499             
8500             this.maskEl.left.setStyle('position', 'absolute');
8501             this.maskEl.left.setStyle('z-index', zIndex);
8502             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8503             this.maskEl.left.setLeft(0);
8504             this.maskEl.left.setTop(box.y - this.padding);
8505             this.maskEl.left.show();
8506
8507             this.maskEl.bottom.setStyle('position', 'absolute');
8508             this.maskEl.bottom.setStyle('z-index', zIndex);
8509             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8510             this.maskEl.bottom.setLeft(0);
8511             this.maskEl.bottom.setTop(box.bottom + this.padding);
8512             this.maskEl.bottom.show();
8513
8514             this.maskEl.right.setStyle('position', 'absolute');
8515             this.maskEl.right.setStyle('z-index', zIndex);
8516             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8517             this.maskEl.right.setLeft(box.right + this.padding);
8518             this.maskEl.right.setTop(box.y - this.padding);
8519             this.maskEl.right.show();
8520
8521             this.toolTip.bindEl = this.target.el;
8522
8523             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8524
8525             var tip = this.target.blankText;
8526
8527             if(this.target.getValue() !== '' ) {
8528                 
8529                 if (this.target.invalidText.length) {
8530                     tip = this.target.invalidText;
8531                 } else if (this.target.regexText.length){
8532                     tip = this.target.regexText;
8533                 }
8534             }
8535
8536             this.toolTip.show(tip);
8537
8538             this.intervalID = window.setInterval(function() {
8539                 Roo.bootstrap.Form.popover.unmask();
8540             }, 10000);
8541
8542             window.onwheel = function(){ return false;};
8543             
8544             (function(){ this.isMasked = true; }).defer(500, this);
8545             
8546         },
8547         
8548         unmask : function()
8549         {
8550             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8551                 return;
8552             }
8553             
8554             this.maskEl.top.setStyle('position', 'absolute');
8555             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8556             this.maskEl.top.hide();
8557
8558             this.maskEl.left.setStyle('position', 'absolute');
8559             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8560             this.maskEl.left.hide();
8561
8562             this.maskEl.bottom.setStyle('position', 'absolute');
8563             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8564             this.maskEl.bottom.hide();
8565
8566             this.maskEl.right.setStyle('position', 'absolute');
8567             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8568             this.maskEl.right.hide();
8569             
8570             this.toolTip.hide();
8571             
8572             this.toolTip.el.hide();
8573             
8574             window.onwheel = function(){ return true;};
8575             
8576             if(this.intervalID){
8577                 window.clearInterval(this.intervalID);
8578                 this.intervalID = false;
8579             }
8580             
8581             this.isMasked = false;
8582             
8583         }
8584         
8585     }
8586     
8587 });
8588
8589 /*
8590  * Based on:
8591  * Ext JS Library 1.1.1
8592  * Copyright(c) 2006-2007, Ext JS, LLC.
8593  *
8594  * Originally Released Under LGPL - original licence link has changed is not relivant.
8595  *
8596  * Fork - LGPL
8597  * <script type="text/javascript">
8598  */
8599 /**
8600  * @class Roo.form.VTypes
8601  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8602  * @singleton
8603  */
8604 Roo.form.VTypes = function(){
8605     // closure these in so they are only created once.
8606     var alpha = /^[a-zA-Z_]+$/;
8607     var alphanum = /^[a-zA-Z0-9_]+$/;
8608     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8609     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8610
8611     // All these messages and functions are configurable
8612     return {
8613         /**
8614          * The function used to validate email addresses
8615          * @param {String} value The email address
8616          */
8617         'email' : function(v){
8618             return email.test(v);
8619         },
8620         /**
8621          * The error text to display when the email validation function returns false
8622          * @type String
8623          */
8624         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8625         /**
8626          * The keystroke filter mask to be applied on email input
8627          * @type RegExp
8628          */
8629         'emailMask' : /[a-z0-9_\.\-@]/i,
8630
8631         /**
8632          * The function used to validate URLs
8633          * @param {String} value The URL
8634          */
8635         'url' : function(v){
8636             return url.test(v);
8637         },
8638         /**
8639          * The error text to display when the url validation function returns false
8640          * @type String
8641          */
8642         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8643         
8644         /**
8645          * The function used to validate alpha values
8646          * @param {String} value The value
8647          */
8648         'alpha' : function(v){
8649             return alpha.test(v);
8650         },
8651         /**
8652          * The error text to display when the alpha validation function returns false
8653          * @type String
8654          */
8655         'alphaText' : 'This field should only contain letters and _',
8656         /**
8657          * The keystroke filter mask to be applied on alpha input
8658          * @type RegExp
8659          */
8660         'alphaMask' : /[a-z_]/i,
8661
8662         /**
8663          * The function used to validate alphanumeric values
8664          * @param {String} value The value
8665          */
8666         'alphanum' : function(v){
8667             return alphanum.test(v);
8668         },
8669         /**
8670          * The error text to display when the alphanumeric validation function returns false
8671          * @type String
8672          */
8673         'alphanumText' : 'This field should only contain letters, numbers and _',
8674         /**
8675          * The keystroke filter mask to be applied on alphanumeric input
8676          * @type RegExp
8677          */
8678         'alphanumMask' : /[a-z0-9_]/i
8679     };
8680 }();/*
8681  * - LGPL
8682  *
8683  * Input
8684  * 
8685  */
8686
8687 /**
8688  * @class Roo.bootstrap.Input
8689  * @extends Roo.bootstrap.Component
8690  * Bootstrap Input class
8691  * @cfg {Boolean} disabled is it disabled
8692  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8693  * @cfg {String} name name of the input
8694  * @cfg {string} fieldLabel - the label associated
8695  * @cfg {string} placeholder - placeholder to put in text.
8696  * @cfg {string}  before - input group add on before
8697  * @cfg {string} after - input group add on after
8698  * @cfg {string} size - (lg|sm) or leave empty..
8699  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8700  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8701  * @cfg {Number} md colspan out of 12 for computer-sized screens
8702  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8703  * @cfg {string} value default value of the input
8704  * @cfg {Number} labelWidth set the width of label 
8705  * @cfg {Number} labellg set the width of label (1-12)
8706  * @cfg {Number} labelmd set the width of label (1-12)
8707  * @cfg {Number} labelsm set the width of label (1-12)
8708  * @cfg {Number} labelxs set the width of label (1-12)
8709  * @cfg {String} labelAlign (top|left)
8710  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8711  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8712  * @cfg {String} indicatorpos (left|right) default left
8713  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8714  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8715
8716  * @cfg {String} align (left|center|right) Default left
8717  * @cfg {Boolean} forceFeedback (true|false) Default false
8718  * 
8719  * @constructor
8720  * Create a new Input
8721  * @param {Object} config The config object
8722  */
8723
8724 Roo.bootstrap.Input = function(config){
8725     
8726     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8727     
8728     this.addEvents({
8729         /**
8730          * @event focus
8731          * Fires when this field receives input focus.
8732          * @param {Roo.form.Field} this
8733          */
8734         focus : true,
8735         /**
8736          * @event blur
8737          * Fires when this field loses input focus.
8738          * @param {Roo.form.Field} this
8739          */
8740         blur : true,
8741         /**
8742          * @event specialkey
8743          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8744          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8745          * @param {Roo.form.Field} this
8746          * @param {Roo.EventObject} e The event object
8747          */
8748         specialkey : true,
8749         /**
8750          * @event change
8751          * Fires just before the field blurs if the field value has changed.
8752          * @param {Roo.form.Field} this
8753          * @param {Mixed} newValue The new value
8754          * @param {Mixed} oldValue The original value
8755          */
8756         change : true,
8757         /**
8758          * @event invalid
8759          * Fires after the field has been marked as invalid.
8760          * @param {Roo.form.Field} this
8761          * @param {String} msg The validation message
8762          */
8763         invalid : true,
8764         /**
8765          * @event valid
8766          * Fires after the field has been validated with no errors.
8767          * @param {Roo.form.Field} this
8768          */
8769         valid : true,
8770          /**
8771          * @event keyup
8772          * Fires after the key up
8773          * @param {Roo.form.Field} this
8774          * @param {Roo.EventObject}  e The event Object
8775          */
8776         keyup : true
8777     });
8778 };
8779
8780 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8781      /**
8782      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8783       automatic validation (defaults to "keyup").
8784      */
8785     validationEvent : "keyup",
8786      /**
8787      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8788      */
8789     validateOnBlur : true,
8790     /**
8791      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8792      */
8793     validationDelay : 250,
8794      /**
8795      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8796      */
8797     focusClass : "x-form-focus",  // not needed???
8798     
8799        
8800     /**
8801      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8802      */
8803     invalidClass : "has-warning",
8804     
8805     /**
8806      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8807      */
8808     validClass : "has-success",
8809     
8810     /**
8811      * @cfg {Boolean} hasFeedback (true|false) default true
8812      */
8813     hasFeedback : true,
8814     
8815     /**
8816      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8817      */
8818     invalidFeedbackClass : "glyphicon-warning-sign",
8819     
8820     /**
8821      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8822      */
8823     validFeedbackClass : "glyphicon-ok",
8824     
8825     /**
8826      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8827      */
8828     selectOnFocus : false,
8829     
8830      /**
8831      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8832      */
8833     maskRe : null,
8834        /**
8835      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8836      */
8837     vtype : null,
8838     
8839       /**
8840      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8841      */
8842     disableKeyFilter : false,
8843     
8844        /**
8845      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8846      */
8847     disabled : false,
8848      /**
8849      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8850      */
8851     allowBlank : true,
8852     /**
8853      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8854      */
8855     blankText : "Please complete this mandatory field",
8856     
8857      /**
8858      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8859      */
8860     minLength : 0,
8861     /**
8862      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8863      */
8864     maxLength : Number.MAX_VALUE,
8865     /**
8866      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8867      */
8868     minLengthText : "The minimum length for this field is {0}",
8869     /**
8870      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8871      */
8872     maxLengthText : "The maximum length for this field is {0}",
8873   
8874     
8875     /**
8876      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8877      * If available, this function will be called only after the basic validators all return true, and will be passed the
8878      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8879      */
8880     validator : null,
8881     /**
8882      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8883      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8884      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8885      */
8886     regex : null,
8887     /**
8888      * @cfg {String} regexText -- Depricated - use Invalid Text
8889      */
8890     regexText : "",
8891     
8892     /**
8893      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8894      */
8895     invalidText : "",
8896     
8897     
8898     
8899     autocomplete: false,
8900     
8901     
8902     fieldLabel : '',
8903     inputType : 'text',
8904     
8905     name : false,
8906     placeholder: false,
8907     before : false,
8908     after : false,
8909     size : false,
8910     hasFocus : false,
8911     preventMark: false,
8912     isFormField : true,
8913     value : '',
8914     labelWidth : 2,
8915     labelAlign : false,
8916     readOnly : false,
8917     align : false,
8918     formatedValue : false,
8919     forceFeedback : false,
8920     
8921     indicatorpos : 'left',
8922     
8923     labellg : 0,
8924     labelmd : 0,
8925     labelsm : 0,
8926     labelxs : 0,
8927     
8928     capture : '',
8929     accept : '',
8930     
8931     parentLabelAlign : function()
8932     {
8933         var parent = this;
8934         while (parent.parent()) {
8935             parent = parent.parent();
8936             if (typeof(parent.labelAlign) !='undefined') {
8937                 return parent.labelAlign;
8938             }
8939         }
8940         return 'left';
8941         
8942     },
8943     
8944     getAutoCreate : function()
8945     {
8946         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8947         
8948         var id = Roo.id();
8949         
8950         var cfg = {};
8951         
8952         if(this.inputType != 'hidden'){
8953             cfg.cls = 'form-group' //input-group
8954         }
8955         
8956         var input =  {
8957             tag: 'input',
8958             id : id,
8959             type : this.inputType,
8960             value : this.value,
8961             cls : 'form-control',
8962             placeholder : this.placeholder || '',
8963             autocomplete : this.autocomplete || 'new-password'
8964         };
8965         
8966         if(this.capture.length){
8967             input.capture = this.capture;
8968         }
8969         
8970         if(this.accept.length){
8971             input.accept = this.accept + "/*";
8972         }
8973         
8974         if(this.align){
8975             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8976         }
8977         
8978         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8979             input.maxLength = this.maxLength;
8980         }
8981         
8982         if (this.disabled) {
8983             input.disabled=true;
8984         }
8985         
8986         if (this.readOnly) {
8987             input.readonly=true;
8988         }
8989         
8990         if (this.name) {
8991             input.name = this.name;
8992         }
8993         
8994         if (this.size) {
8995             input.cls += ' input-' + this.size;
8996         }
8997         
8998         var settings=this;
8999         ['xs','sm','md','lg'].map(function(size){
9000             if (settings[size]) {
9001                 cfg.cls += ' col-' + size + '-' + settings[size];
9002             }
9003         });
9004         
9005         var inputblock = input;
9006         
9007         var feedback = {
9008             tag: 'span',
9009             cls: 'glyphicon form-control-feedback'
9010         };
9011             
9012         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9013             
9014             inputblock = {
9015                 cls : 'has-feedback',
9016                 cn :  [
9017                     input,
9018                     feedback
9019                 ] 
9020             };  
9021         }
9022         
9023         if (this.before || this.after) {
9024             
9025             inputblock = {
9026                 cls : 'input-group',
9027                 cn :  [] 
9028             };
9029             
9030             if (this.before && typeof(this.before) == 'string') {
9031                 
9032                 inputblock.cn.push({
9033                     tag :'span',
9034                     cls : 'roo-input-before input-group-addon',
9035                     html : this.before
9036                 });
9037             }
9038             if (this.before && typeof(this.before) == 'object') {
9039                 this.before = Roo.factory(this.before);
9040                 
9041                 inputblock.cn.push({
9042                     tag :'span',
9043                     cls : 'roo-input-before input-group-' +
9044                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9045                 });
9046             }
9047             
9048             inputblock.cn.push(input);
9049             
9050             if (this.after && typeof(this.after) == 'string') {
9051                 inputblock.cn.push({
9052                     tag :'span',
9053                     cls : 'roo-input-after input-group-addon',
9054                     html : this.after
9055                 });
9056             }
9057             if (this.after && typeof(this.after) == 'object') {
9058                 this.after = Roo.factory(this.after);
9059                 
9060                 inputblock.cn.push({
9061                     tag :'span',
9062                     cls : 'roo-input-after input-group-' +
9063                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9064                 });
9065             }
9066             
9067             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9068                 inputblock.cls += ' has-feedback';
9069                 inputblock.cn.push(feedback);
9070             }
9071         };
9072         
9073         if (align ==='left' && this.fieldLabel.length) {
9074             
9075             cfg.cls += ' roo-form-group-label-left';
9076             
9077             cfg.cn = [
9078                 {
9079                     tag : 'i',
9080                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9081                     tooltip : 'This field is required'
9082                 },
9083                 {
9084                     tag: 'label',
9085                     'for' :  id,
9086                     cls : 'control-label',
9087                     html : this.fieldLabel
9088
9089                 },
9090                 {
9091                     cls : "", 
9092                     cn: [
9093                         inputblock
9094                     ]
9095                 }
9096             ];
9097             
9098             var labelCfg = cfg.cn[1];
9099             var contentCfg = cfg.cn[2];
9100             
9101             if(this.indicatorpos == 'right'){
9102                 cfg.cn = [
9103                     {
9104                         tag: 'label',
9105                         'for' :  id,
9106                         cls : 'control-label',
9107                         cn : [
9108                             {
9109                                 tag : 'span',
9110                                 html : this.fieldLabel
9111                             },
9112                             {
9113                                 tag : 'i',
9114                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9115                                 tooltip : 'This field is required'
9116                             }
9117                         ]
9118                     },
9119                     {
9120                         cls : "",
9121                         cn: [
9122                             inputblock
9123                         ]
9124                     }
9125
9126                 ];
9127                 
9128                 labelCfg = cfg.cn[0];
9129                 contentCfg = cfg.cn[1];
9130             
9131             }
9132             
9133             if(this.labelWidth > 12){
9134                 labelCfg.style = "width: " + this.labelWidth + 'px';
9135             }
9136             
9137             if(this.labelWidth < 13 && this.labelmd == 0){
9138                 this.labelmd = this.labelWidth;
9139             }
9140             
9141             if(this.labellg > 0){
9142                 labelCfg.cls += ' col-lg-' + this.labellg;
9143                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9144             }
9145             
9146             if(this.labelmd > 0){
9147                 labelCfg.cls += ' col-md-' + this.labelmd;
9148                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9149             }
9150             
9151             if(this.labelsm > 0){
9152                 labelCfg.cls += ' col-sm-' + this.labelsm;
9153                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9154             }
9155             
9156             if(this.labelxs > 0){
9157                 labelCfg.cls += ' col-xs-' + this.labelxs;
9158                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9159             }
9160             
9161             
9162         } else if ( this.fieldLabel.length) {
9163                 
9164             cfg.cn = [
9165                 {
9166                     tag : 'i',
9167                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9168                     tooltip : 'This field is required'
9169                 },
9170                 {
9171                     tag: 'label',
9172                    //cls : 'input-group-addon',
9173                     html : this.fieldLabel
9174
9175                 },
9176
9177                inputblock
9178
9179            ];
9180            
9181            if(this.indicatorpos == 'right'){
9182                 
9183                 cfg.cn = [
9184                     {
9185                         tag: 'label',
9186                        //cls : 'input-group-addon',
9187                         html : this.fieldLabel
9188
9189                     },
9190                     {
9191                         tag : 'i',
9192                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9193                         tooltip : 'This field is required'
9194                     },
9195
9196                    inputblock
9197
9198                ];
9199
9200             }
9201
9202         } else {
9203             
9204             cfg.cn = [
9205
9206                     inputblock
9207
9208             ];
9209                 
9210                 
9211         };
9212         
9213         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9214            cfg.cls += ' navbar-form';
9215         }
9216         
9217         if (this.parentType === 'NavGroup') {
9218            cfg.cls += ' navbar-form';
9219            cfg.tag = 'li';
9220         }
9221         
9222         return cfg;
9223         
9224     },
9225     /**
9226      * return the real input element.
9227      */
9228     inputEl: function ()
9229     {
9230         return this.el.select('input.form-control',true).first();
9231     },
9232     
9233     tooltipEl : function()
9234     {
9235         return this.inputEl();
9236     },
9237     
9238     indicatorEl : function()
9239     {
9240         var indicator = this.el.select('i.roo-required-indicator',true).first();
9241         
9242         if(!indicator){
9243             return false;
9244         }
9245         
9246         return indicator;
9247         
9248     },
9249     
9250     setDisabled : function(v)
9251     {
9252         var i  = this.inputEl().dom;
9253         if (!v) {
9254             i.removeAttribute('disabled');
9255             return;
9256             
9257         }
9258         i.setAttribute('disabled','true');
9259     },
9260     initEvents : function()
9261     {
9262           
9263         this.inputEl().on("keydown" , this.fireKey,  this);
9264         this.inputEl().on("focus", this.onFocus,  this);
9265         this.inputEl().on("blur", this.onBlur,  this);
9266         
9267         this.inputEl().relayEvent('keyup', this);
9268         
9269         this.indicator = this.indicatorEl();
9270         
9271         if(this.indicator){
9272             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9273         }
9274  
9275         // reference to original value for reset
9276         this.originalValue = this.getValue();
9277         //Roo.form.TextField.superclass.initEvents.call(this);
9278         if(this.validationEvent == 'keyup'){
9279             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9280             this.inputEl().on('keyup', this.filterValidation, this);
9281         }
9282         else if(this.validationEvent !== false){
9283             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9284         }
9285         
9286         if(this.selectOnFocus){
9287             this.on("focus", this.preFocus, this);
9288             
9289         }
9290         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9291             this.inputEl().on("keypress", this.filterKeys, this);
9292         } else {
9293             this.inputEl().relayEvent('keypress', this);
9294         }
9295        /* if(this.grow){
9296             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9297             this.el.on("click", this.autoSize,  this);
9298         }
9299         */
9300         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9301             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9302         }
9303         
9304         if (typeof(this.before) == 'object') {
9305             this.before.render(this.el.select('.roo-input-before',true).first());
9306         }
9307         if (typeof(this.after) == 'object') {
9308             this.after.render(this.el.select('.roo-input-after',true).first());
9309         }
9310         
9311         this.inputEl().on('change', this.onChange, this);
9312         
9313     },
9314     filterValidation : function(e){
9315         if(!e.isNavKeyPress()){
9316             this.validationTask.delay(this.validationDelay);
9317         }
9318     },
9319      /**
9320      * Validates the field value
9321      * @return {Boolean} True if the value is valid, else false
9322      */
9323     validate : function(){
9324         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9325         if(this.disabled || this.validateValue(this.getRawValue())){
9326             this.markValid();
9327             return true;
9328         }
9329         
9330         this.markInvalid();
9331         return false;
9332     },
9333     
9334     
9335     /**
9336      * Validates a value according to the field's validation rules and marks the field as invalid
9337      * if the validation fails
9338      * @param {Mixed} value The value to validate
9339      * @return {Boolean} True if the value is valid, else false
9340      */
9341     validateValue : function(value)
9342     {
9343         if(this.getVisibilityEl().hasClass('hidden')){
9344             return true;
9345         }
9346         
9347         if(value.length < 1)  { // if it's blank
9348             if(this.allowBlank){
9349                 return true;
9350             }
9351             return false;
9352         }
9353         
9354         if(value.length < this.minLength){
9355             return false;
9356         }
9357         if(value.length > this.maxLength){
9358             return false;
9359         }
9360         if(this.vtype){
9361             var vt = Roo.form.VTypes;
9362             if(!vt[this.vtype](value, this)){
9363                 return false;
9364             }
9365         }
9366         if(typeof this.validator == "function"){
9367             var msg = this.validator(value);
9368             if(msg !== true){
9369                 return false;
9370             }
9371             if (typeof(msg) == 'string') {
9372                 this.invalidText = msg;
9373             }
9374         }
9375         
9376         if(this.regex && !this.regex.test(value)){
9377             return false;
9378         }
9379         
9380         return true;
9381     },
9382     
9383      // private
9384     fireKey : function(e){
9385         //Roo.log('field ' + e.getKey());
9386         if(e.isNavKeyPress()){
9387             this.fireEvent("specialkey", this, e);
9388         }
9389     },
9390     focus : function (selectText){
9391         if(this.rendered){
9392             this.inputEl().focus();
9393             if(selectText === true){
9394                 this.inputEl().dom.select();
9395             }
9396         }
9397         return this;
9398     } ,
9399     
9400     onFocus : function(){
9401         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9402            // this.el.addClass(this.focusClass);
9403         }
9404         if(!this.hasFocus){
9405             this.hasFocus = true;
9406             this.startValue = this.getValue();
9407             this.fireEvent("focus", this);
9408         }
9409     },
9410     
9411     beforeBlur : Roo.emptyFn,
9412
9413     
9414     // private
9415     onBlur : function(){
9416         this.beforeBlur();
9417         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9418             //this.el.removeClass(this.focusClass);
9419         }
9420         this.hasFocus = false;
9421         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9422             this.validate();
9423         }
9424         var v = this.getValue();
9425         if(String(v) !== String(this.startValue)){
9426             this.fireEvent('change', this, v, this.startValue);
9427         }
9428         this.fireEvent("blur", this);
9429     },
9430     
9431     onChange : function(e)
9432     {
9433         var v = this.getValue();
9434         if(String(v) !== String(this.startValue)){
9435             this.fireEvent('change', this, v, this.startValue);
9436         }
9437         
9438     },
9439     
9440     /**
9441      * Resets the current field value to the originally loaded value and clears any validation messages
9442      */
9443     reset : function(){
9444         this.setValue(this.originalValue);
9445         this.validate();
9446     },
9447      /**
9448      * Returns the name of the field
9449      * @return {Mixed} name The name field
9450      */
9451     getName: function(){
9452         return this.name;
9453     },
9454      /**
9455      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9456      * @return {Mixed} value The field value
9457      */
9458     getValue : function(){
9459         
9460         var v = this.inputEl().getValue();
9461         
9462         return v;
9463     },
9464     /**
9465      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9466      * @return {Mixed} value The field value
9467      */
9468     getRawValue : function(){
9469         var v = this.inputEl().getValue();
9470         
9471         return v;
9472     },
9473     
9474     /**
9475      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9476      * @param {Mixed} value The value to set
9477      */
9478     setRawValue : function(v){
9479         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9480     },
9481     
9482     selectText : function(start, end){
9483         var v = this.getRawValue();
9484         if(v.length > 0){
9485             start = start === undefined ? 0 : start;
9486             end = end === undefined ? v.length : end;
9487             var d = this.inputEl().dom;
9488             if(d.setSelectionRange){
9489                 d.setSelectionRange(start, end);
9490             }else if(d.createTextRange){
9491                 var range = d.createTextRange();
9492                 range.moveStart("character", start);
9493                 range.moveEnd("character", v.length-end);
9494                 range.select();
9495             }
9496         }
9497     },
9498     
9499     /**
9500      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9501      * @param {Mixed} value The value to set
9502      */
9503     setValue : function(v){
9504         this.value = v;
9505         if(this.rendered){
9506             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9507             this.validate();
9508         }
9509     },
9510     
9511     /*
9512     processValue : function(value){
9513         if(this.stripCharsRe){
9514             var newValue = value.replace(this.stripCharsRe, '');
9515             if(newValue !== value){
9516                 this.setRawValue(newValue);
9517                 return newValue;
9518             }
9519         }
9520         return value;
9521     },
9522   */
9523     preFocus : function(){
9524         
9525         if(this.selectOnFocus){
9526             this.inputEl().dom.select();
9527         }
9528     },
9529     filterKeys : function(e){
9530         var k = e.getKey();
9531         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9532             return;
9533         }
9534         var c = e.getCharCode(), cc = String.fromCharCode(c);
9535         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9536             return;
9537         }
9538         if(!this.maskRe.test(cc)){
9539             e.stopEvent();
9540         }
9541     },
9542      /**
9543      * Clear any invalid styles/messages for this field
9544      */
9545     clearInvalid : function(){
9546         
9547         if(!this.el || this.preventMark){ // not rendered
9548             return;
9549         }
9550         
9551      
9552         this.el.removeClass(this.invalidClass);
9553         
9554         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9555             
9556             var feedback = this.el.select('.form-control-feedback', true).first();
9557             
9558             if(feedback){
9559                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9560             }
9561             
9562         }
9563         
9564         if(this.indicator){
9565             this.indicator.removeClass('visible');
9566             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9567         }
9568         
9569         this.fireEvent('valid', this);
9570     },
9571     
9572      /**
9573      * Mark this field as valid
9574      */
9575     markValid : function()
9576     {
9577         if(!this.el  || this.preventMark){ // not rendered...
9578             return;
9579         }
9580         
9581         this.el.removeClass([this.invalidClass, this.validClass]);
9582         
9583         var feedback = this.el.select('.form-control-feedback', true).first();
9584             
9585         if(feedback){
9586             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9587         }
9588         
9589         if(this.indicator){
9590             this.indicator.removeClass('visible');
9591             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9592         }
9593         
9594         if(this.disabled){
9595             return;
9596         }
9597         
9598         if(this.allowBlank && !this.getRawValue().length){
9599             return;
9600         }
9601         
9602         this.el.addClass(this.validClass);
9603         
9604         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9605             
9606             var feedback = this.el.select('.form-control-feedback', true).first();
9607             
9608             if(feedback){
9609                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9610                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9611             }
9612             
9613         }
9614         
9615         this.fireEvent('valid', this);
9616     },
9617     
9618      /**
9619      * Mark this field as invalid
9620      * @param {String} msg The validation message
9621      */
9622     markInvalid : function(msg)
9623     {
9624         if(!this.el  || this.preventMark){ // not rendered
9625             return;
9626         }
9627         
9628         this.el.removeClass([this.invalidClass, this.validClass]);
9629         
9630         var feedback = this.el.select('.form-control-feedback', true).first();
9631             
9632         if(feedback){
9633             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9634         }
9635
9636         if(this.disabled){
9637             return;
9638         }
9639         
9640         if(this.allowBlank && !this.getRawValue().length){
9641             return;
9642         }
9643         
9644         if(this.indicator){
9645             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9646             this.indicator.addClass('visible');
9647         }
9648         
9649         this.el.addClass(this.invalidClass);
9650         
9651         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9652             
9653             var feedback = this.el.select('.form-control-feedback', true).first();
9654             
9655             if(feedback){
9656                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9657                 
9658                 if(this.getValue().length || this.forceFeedback){
9659                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9660                 }
9661                 
9662             }
9663             
9664         }
9665         
9666         this.fireEvent('invalid', this, msg);
9667     },
9668     // private
9669     SafariOnKeyDown : function(event)
9670     {
9671         // this is a workaround for a password hang bug on chrome/ webkit.
9672         if (this.inputEl().dom.type != 'password') {
9673             return;
9674         }
9675         
9676         var isSelectAll = false;
9677         
9678         if(this.inputEl().dom.selectionEnd > 0){
9679             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9680         }
9681         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9682             event.preventDefault();
9683             this.setValue('');
9684             return;
9685         }
9686         
9687         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9688             
9689             event.preventDefault();
9690             // this is very hacky as keydown always get's upper case.
9691             //
9692             var cc = String.fromCharCode(event.getCharCode());
9693             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9694             
9695         }
9696     },
9697     adjustWidth : function(tag, w){
9698         tag = tag.toLowerCase();
9699         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9700             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9701                 if(tag == 'input'){
9702                     return w + 2;
9703                 }
9704                 if(tag == 'textarea'){
9705                     return w-2;
9706                 }
9707             }else if(Roo.isOpera){
9708                 if(tag == 'input'){
9709                     return w + 2;
9710                 }
9711                 if(tag == 'textarea'){
9712                     return w-2;
9713                 }
9714             }
9715         }
9716         return w;
9717     },
9718     
9719     setFieldLabel : function(v)
9720     {
9721         if(!this.rendered){
9722             return;
9723         }
9724         
9725         if(this.indicator){
9726             var ar = this.el.select('label > span',true);
9727             
9728             if (ar.elements.length) {
9729                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9730                 this.fieldLabel = v;
9731                 return;
9732             }
9733             
9734             var br = this.el.select('label',true);
9735             
9736             if(br.elements.length) {
9737                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9738                 this.fieldLabel = v;
9739                 return;
9740             }
9741             
9742             Roo.log('Cannot Found any of label > span || label in input');
9743             return;
9744         }
9745         
9746         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9747         this.fieldLabel = v;
9748         
9749         
9750     }
9751 });
9752
9753  
9754 /*
9755  * - LGPL
9756  *
9757  * Input
9758  * 
9759  */
9760
9761 /**
9762  * @class Roo.bootstrap.TextArea
9763  * @extends Roo.bootstrap.Input
9764  * Bootstrap TextArea class
9765  * @cfg {Number} cols Specifies the visible width of a text area
9766  * @cfg {Number} rows Specifies the visible number of lines in a text area
9767  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9768  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9769  * @cfg {string} html text
9770  * 
9771  * @constructor
9772  * Create a new TextArea
9773  * @param {Object} config The config object
9774  */
9775
9776 Roo.bootstrap.TextArea = function(config){
9777     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9778    
9779 };
9780
9781 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9782      
9783     cols : false,
9784     rows : 5,
9785     readOnly : false,
9786     warp : 'soft',
9787     resize : false,
9788     value: false,
9789     html: false,
9790     
9791     getAutoCreate : function(){
9792         
9793         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9794         
9795         var id = Roo.id();
9796         
9797         var cfg = {};
9798         
9799         if(this.inputType != 'hidden'){
9800             cfg.cls = 'form-group' //input-group
9801         }
9802         
9803         var input =  {
9804             tag: 'textarea',
9805             id : id,
9806             warp : this.warp,
9807             rows : this.rows,
9808             value : this.value || '',
9809             html: this.html || '',
9810             cls : 'form-control',
9811             placeholder : this.placeholder || '' 
9812             
9813         };
9814         
9815         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9816             input.maxLength = this.maxLength;
9817         }
9818         
9819         if(this.resize){
9820             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9821         }
9822         
9823         if(this.cols){
9824             input.cols = this.cols;
9825         }
9826         
9827         if (this.readOnly) {
9828             input.readonly = true;
9829         }
9830         
9831         if (this.name) {
9832             input.name = this.name;
9833         }
9834         
9835         if (this.size) {
9836             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9837         }
9838         
9839         var settings=this;
9840         ['xs','sm','md','lg'].map(function(size){
9841             if (settings[size]) {
9842                 cfg.cls += ' col-' + size + '-' + settings[size];
9843             }
9844         });
9845         
9846         var inputblock = input;
9847         
9848         if(this.hasFeedback && !this.allowBlank){
9849             
9850             var feedback = {
9851                 tag: 'span',
9852                 cls: 'glyphicon form-control-feedback'
9853             };
9854
9855             inputblock = {
9856                 cls : 'has-feedback',
9857                 cn :  [
9858                     input,
9859                     feedback
9860                 ] 
9861             };  
9862         }
9863         
9864         
9865         if (this.before || this.after) {
9866             
9867             inputblock = {
9868                 cls : 'input-group',
9869                 cn :  [] 
9870             };
9871             if (this.before) {
9872                 inputblock.cn.push({
9873                     tag :'span',
9874                     cls : 'input-group-addon',
9875                     html : this.before
9876                 });
9877             }
9878             
9879             inputblock.cn.push(input);
9880             
9881             if(this.hasFeedback && !this.allowBlank){
9882                 inputblock.cls += ' has-feedback';
9883                 inputblock.cn.push(feedback);
9884             }
9885             
9886             if (this.after) {
9887                 inputblock.cn.push({
9888                     tag :'span',
9889                     cls : 'input-group-addon',
9890                     html : this.after
9891                 });
9892             }
9893             
9894         }
9895         
9896         if (align ==='left' && this.fieldLabel.length) {
9897             cfg.cn = [
9898                 {
9899                     tag: 'label',
9900                     'for' :  id,
9901                     cls : 'control-label',
9902                     html : this.fieldLabel
9903                 },
9904                 {
9905                     cls : "",
9906                     cn: [
9907                         inputblock
9908                     ]
9909                 }
9910
9911             ];
9912             
9913             if(this.labelWidth > 12){
9914                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9915             }
9916
9917             if(this.labelWidth < 13 && this.labelmd == 0){
9918                 this.labelmd = this.labelWidth;
9919             }
9920
9921             if(this.labellg > 0){
9922                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9923                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9924             }
9925
9926             if(this.labelmd > 0){
9927                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9928                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9929             }
9930
9931             if(this.labelsm > 0){
9932                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9933                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9934             }
9935
9936             if(this.labelxs > 0){
9937                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9938                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9939             }
9940             
9941         } else if ( this.fieldLabel.length) {
9942             cfg.cn = [
9943
9944                {
9945                    tag: 'label',
9946                    //cls : 'input-group-addon',
9947                    html : this.fieldLabel
9948
9949                },
9950
9951                inputblock
9952
9953            ];
9954
9955         } else {
9956
9957             cfg.cn = [
9958
9959                 inputblock
9960
9961             ];
9962                 
9963         }
9964         
9965         if (this.disabled) {
9966             input.disabled=true;
9967         }
9968         
9969         return cfg;
9970         
9971     },
9972     /**
9973      * return the real textarea element.
9974      */
9975     inputEl: function ()
9976     {
9977         return this.el.select('textarea.form-control',true).first();
9978     },
9979     
9980     /**
9981      * Clear any invalid styles/messages for this field
9982      */
9983     clearInvalid : function()
9984     {
9985         
9986         if(!this.el || this.preventMark){ // not rendered
9987             return;
9988         }
9989         
9990         var label = this.el.select('label', true).first();
9991         var icon = this.el.select('i.fa-star', true).first();
9992         
9993         if(label && icon){
9994             icon.remove();
9995         }
9996         
9997         this.el.removeClass(this.invalidClass);
9998         
9999         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10000             
10001             var feedback = this.el.select('.form-control-feedback', true).first();
10002             
10003             if(feedback){
10004                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10005             }
10006             
10007         }
10008         
10009         this.fireEvent('valid', this);
10010     },
10011     
10012      /**
10013      * Mark this field as valid
10014      */
10015     markValid : function()
10016     {
10017         if(!this.el  || this.preventMark){ // not rendered
10018             return;
10019         }
10020         
10021         this.el.removeClass([this.invalidClass, this.validClass]);
10022         
10023         var feedback = this.el.select('.form-control-feedback', true).first();
10024             
10025         if(feedback){
10026             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10027         }
10028
10029         if(this.disabled || this.allowBlank){
10030             return;
10031         }
10032         
10033         var label = this.el.select('label', true).first();
10034         var icon = this.el.select('i.fa-star', true).first();
10035         
10036         if(label && icon){
10037             icon.remove();
10038         }
10039         
10040         this.el.addClass(this.validClass);
10041         
10042         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10043             
10044             var feedback = this.el.select('.form-control-feedback', true).first();
10045             
10046             if(feedback){
10047                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10048                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10049             }
10050             
10051         }
10052         
10053         this.fireEvent('valid', this);
10054     },
10055     
10056      /**
10057      * Mark this field as invalid
10058      * @param {String} msg The validation message
10059      */
10060     markInvalid : function(msg)
10061     {
10062         if(!this.el  || this.preventMark){ // not rendered
10063             return;
10064         }
10065         
10066         this.el.removeClass([this.invalidClass, this.validClass]);
10067         
10068         var feedback = this.el.select('.form-control-feedback', true).first();
10069             
10070         if(feedback){
10071             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10072         }
10073
10074         if(this.disabled || this.allowBlank){
10075             return;
10076         }
10077         
10078         var label = this.el.select('label', true).first();
10079         var icon = this.el.select('i.fa-star', true).first();
10080         
10081         if(!this.getValue().length && label && !icon){
10082             this.el.createChild({
10083                 tag : 'i',
10084                 cls : 'text-danger fa fa-lg fa-star',
10085                 tooltip : 'This field is required',
10086                 style : 'margin-right:5px;'
10087             }, label, true);
10088         }
10089
10090         this.el.addClass(this.invalidClass);
10091         
10092         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10093             
10094             var feedback = this.el.select('.form-control-feedback', true).first();
10095             
10096             if(feedback){
10097                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10098                 
10099                 if(this.getValue().length || this.forceFeedback){
10100                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10101                 }
10102                 
10103             }
10104             
10105         }
10106         
10107         this.fireEvent('invalid', this, msg);
10108     }
10109 });
10110
10111  
10112 /*
10113  * - LGPL
10114  *
10115  * trigger field - base class for combo..
10116  * 
10117  */
10118  
10119 /**
10120  * @class Roo.bootstrap.TriggerField
10121  * @extends Roo.bootstrap.Input
10122  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10123  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10124  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10125  * for which you can provide a custom implementation.  For example:
10126  * <pre><code>
10127 var trigger = new Roo.bootstrap.TriggerField();
10128 trigger.onTriggerClick = myTriggerFn;
10129 trigger.applyTo('my-field');
10130 </code></pre>
10131  *
10132  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10133  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10134  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10135  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10136  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10137
10138  * @constructor
10139  * Create a new TriggerField.
10140  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10141  * to the base TextField)
10142  */
10143 Roo.bootstrap.TriggerField = function(config){
10144     this.mimicing = false;
10145     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10146 };
10147
10148 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10149     /**
10150      * @cfg {String} triggerClass A CSS class to apply to the trigger
10151      */
10152      /**
10153      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10154      */
10155     hideTrigger:false,
10156
10157     /**
10158      * @cfg {Boolean} removable (true|false) special filter default false
10159      */
10160     removable : false,
10161     
10162     /** @cfg {Boolean} grow @hide */
10163     /** @cfg {Number} growMin @hide */
10164     /** @cfg {Number} growMax @hide */
10165
10166     /**
10167      * @hide 
10168      * @method
10169      */
10170     autoSize: Roo.emptyFn,
10171     // private
10172     monitorTab : true,
10173     // private
10174     deferHeight : true,
10175
10176     
10177     actionMode : 'wrap',
10178     
10179     caret : false,
10180     
10181     
10182     getAutoCreate : function(){
10183        
10184         var align = this.labelAlign || this.parentLabelAlign();
10185         
10186         var id = Roo.id();
10187         
10188         var cfg = {
10189             cls: 'form-group' //input-group
10190         };
10191         
10192         
10193         var input =  {
10194             tag: 'input',
10195             id : id,
10196             type : this.inputType,
10197             cls : 'form-control',
10198             autocomplete: 'new-password',
10199             placeholder : this.placeholder || '' 
10200             
10201         };
10202         if (this.name) {
10203             input.name = this.name;
10204         }
10205         if (this.size) {
10206             input.cls += ' input-' + this.size;
10207         }
10208         
10209         if (this.disabled) {
10210             input.disabled=true;
10211         }
10212         
10213         var inputblock = input;
10214         
10215         if(this.hasFeedback && !this.allowBlank){
10216             
10217             var feedback = {
10218                 tag: 'span',
10219                 cls: 'glyphicon form-control-feedback'
10220             };
10221             
10222             if(this.removable && !this.editable && !this.tickable){
10223                 inputblock = {
10224                     cls : 'has-feedback',
10225                     cn :  [
10226                         inputblock,
10227                         {
10228                             tag: 'button',
10229                             html : 'x',
10230                             cls : 'roo-combo-removable-btn close'
10231                         },
10232                         feedback
10233                     ] 
10234                 };
10235             } else {
10236                 inputblock = {
10237                     cls : 'has-feedback',
10238                     cn :  [
10239                         inputblock,
10240                         feedback
10241                     ] 
10242                 };
10243             }
10244
10245         } else {
10246             if(this.removable && !this.editable && !this.tickable){
10247                 inputblock = {
10248                     cls : 'roo-removable',
10249                     cn :  [
10250                         inputblock,
10251                         {
10252                             tag: 'button',
10253                             html : 'x',
10254                             cls : 'roo-combo-removable-btn close'
10255                         }
10256                     ] 
10257                 };
10258             }
10259         }
10260         
10261         if (this.before || this.after) {
10262             
10263             inputblock = {
10264                 cls : 'input-group',
10265                 cn :  [] 
10266             };
10267             if (this.before) {
10268                 inputblock.cn.push({
10269                     tag :'span',
10270                     cls : 'input-group-addon',
10271                     html : this.before
10272                 });
10273             }
10274             
10275             inputblock.cn.push(input);
10276             
10277             if(this.hasFeedback && !this.allowBlank){
10278                 inputblock.cls += ' has-feedback';
10279                 inputblock.cn.push(feedback);
10280             }
10281             
10282             if (this.after) {
10283                 inputblock.cn.push({
10284                     tag :'span',
10285                     cls : 'input-group-addon',
10286                     html : this.after
10287                 });
10288             }
10289             
10290         };
10291         
10292         var box = {
10293             tag: 'div',
10294             cn: [
10295                 {
10296                     tag: 'input',
10297                     type : 'hidden',
10298                     cls: 'form-hidden-field'
10299                 },
10300                 inputblock
10301             ]
10302             
10303         };
10304         
10305         if(this.multiple){
10306             box = {
10307                 tag: 'div',
10308                 cn: [
10309                     {
10310                         tag: 'input',
10311                         type : 'hidden',
10312                         cls: 'form-hidden-field'
10313                     },
10314                     {
10315                         tag: 'ul',
10316                         cls: 'roo-select2-choices',
10317                         cn:[
10318                             {
10319                                 tag: 'li',
10320                                 cls: 'roo-select2-search-field',
10321                                 cn: [
10322
10323                                     inputblock
10324                                 ]
10325                             }
10326                         ]
10327                     }
10328                 ]
10329             }
10330         };
10331         
10332         var combobox = {
10333             cls: 'roo-select2-container input-group',
10334             cn: [
10335                 box
10336 //                {
10337 //                    tag: 'ul',
10338 //                    cls: 'typeahead typeahead-long dropdown-menu',
10339 //                    style: 'display:none'
10340 //                }
10341             ]
10342         };
10343         
10344         if(!this.multiple && this.showToggleBtn){
10345             
10346             var caret = {
10347                         tag: 'span',
10348                         cls: 'caret'
10349              };
10350             if (this.caret != false) {
10351                 caret = {
10352                      tag: 'i',
10353                      cls: 'fa fa-' + this.caret
10354                 };
10355                 
10356             }
10357             
10358             combobox.cn.push({
10359                 tag :'span',
10360                 cls : 'input-group-addon btn dropdown-toggle',
10361                 cn : [
10362                     caret,
10363                     {
10364                         tag: 'span',
10365                         cls: 'combobox-clear',
10366                         cn  : [
10367                             {
10368                                 tag : 'i',
10369                                 cls: 'icon-remove'
10370                             }
10371                         ]
10372                     }
10373                 ]
10374
10375             })
10376         }
10377         
10378         if(this.multiple){
10379             combobox.cls += ' roo-select2-container-multi';
10380         }
10381         
10382         if (align ==='left' && this.fieldLabel.length) {
10383             
10384             cfg.cls += ' roo-form-group-label-left';
10385
10386             cfg.cn = [
10387                 {
10388                     tag : 'i',
10389                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10390                     tooltip : 'This field is required'
10391                 },
10392                 {
10393                     tag: 'label',
10394                     'for' :  id,
10395                     cls : 'control-label',
10396                     html : this.fieldLabel
10397
10398                 },
10399                 {
10400                     cls : "", 
10401                     cn: [
10402                         combobox
10403                     ]
10404                 }
10405
10406             ];
10407             
10408             var labelCfg = cfg.cn[1];
10409             var contentCfg = cfg.cn[2];
10410             
10411             if(this.indicatorpos == 'right'){
10412                 cfg.cn = [
10413                     {
10414                         tag: 'label',
10415                         'for' :  id,
10416                         cls : 'control-label',
10417                         cn : [
10418                             {
10419                                 tag : 'span',
10420                                 html : this.fieldLabel
10421                             },
10422                             {
10423                                 tag : 'i',
10424                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10425                                 tooltip : 'This field is required'
10426                             }
10427                         ]
10428                     },
10429                     {
10430                         cls : "", 
10431                         cn: [
10432                             combobox
10433                         ]
10434                     }
10435
10436                 ];
10437                 
10438                 labelCfg = cfg.cn[0];
10439                 contentCfg = cfg.cn[1];
10440             }
10441             
10442             if(this.labelWidth > 12){
10443                 labelCfg.style = "width: " + this.labelWidth + 'px';
10444             }
10445             
10446             if(this.labelWidth < 13 && this.labelmd == 0){
10447                 this.labelmd = this.labelWidth;
10448             }
10449             
10450             if(this.labellg > 0){
10451                 labelCfg.cls += ' col-lg-' + this.labellg;
10452                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10453             }
10454             
10455             if(this.labelmd > 0){
10456                 labelCfg.cls += ' col-md-' + this.labelmd;
10457                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10458             }
10459             
10460             if(this.labelsm > 0){
10461                 labelCfg.cls += ' col-sm-' + this.labelsm;
10462                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10463             }
10464             
10465             if(this.labelxs > 0){
10466                 labelCfg.cls += ' col-xs-' + this.labelxs;
10467                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10468             }
10469             
10470         } else if ( this.fieldLabel.length) {
10471 //                Roo.log(" label");
10472             cfg.cn = [
10473                 {
10474                    tag : 'i',
10475                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10476                    tooltip : 'This field is required'
10477                },
10478                {
10479                    tag: 'label',
10480                    //cls : 'input-group-addon',
10481                    html : this.fieldLabel
10482
10483                },
10484
10485                combobox
10486
10487             ];
10488             
10489             if(this.indicatorpos == 'right'){
10490                 
10491                 cfg.cn = [
10492                     {
10493                        tag: 'label',
10494                        cn : [
10495                            {
10496                                tag : 'span',
10497                                html : this.fieldLabel
10498                            },
10499                            {
10500                               tag : 'i',
10501                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10502                               tooltip : 'This field is required'
10503                            }
10504                        ]
10505
10506                     },
10507                     combobox
10508
10509                 ];
10510
10511             }
10512
10513         } else {
10514             
10515 //                Roo.log(" no label && no align");
10516                 cfg = combobox
10517                      
10518                 
10519         }
10520         
10521         var settings=this;
10522         ['xs','sm','md','lg'].map(function(size){
10523             if (settings[size]) {
10524                 cfg.cls += ' col-' + size + '-' + settings[size];
10525             }
10526         });
10527         
10528         return cfg;
10529         
10530     },
10531     
10532     
10533     
10534     // private
10535     onResize : function(w, h){
10536 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10537 //        if(typeof w == 'number'){
10538 //            var x = w - this.trigger.getWidth();
10539 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10540 //            this.trigger.setStyle('left', x+'px');
10541 //        }
10542     },
10543
10544     // private
10545     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10546
10547     // private
10548     getResizeEl : function(){
10549         return this.inputEl();
10550     },
10551
10552     // private
10553     getPositionEl : function(){
10554         return this.inputEl();
10555     },
10556
10557     // private
10558     alignErrorIcon : function(){
10559         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10560     },
10561
10562     // private
10563     initEvents : function(){
10564         
10565         this.createList();
10566         
10567         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10568         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10569         if(!this.multiple && this.showToggleBtn){
10570             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10571             if(this.hideTrigger){
10572                 this.trigger.setDisplayed(false);
10573             }
10574             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10575         }
10576         
10577         if(this.multiple){
10578             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10579         }
10580         
10581         if(this.removable && !this.editable && !this.tickable){
10582             var close = this.closeTriggerEl();
10583             
10584             if(close){
10585                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10586                 close.on('click', this.removeBtnClick, this, close);
10587             }
10588         }
10589         
10590         //this.trigger.addClassOnOver('x-form-trigger-over');
10591         //this.trigger.addClassOnClick('x-form-trigger-click');
10592         
10593         //if(!this.width){
10594         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10595         //}
10596     },
10597     
10598     closeTriggerEl : function()
10599     {
10600         var close = this.el.select('.roo-combo-removable-btn', true).first();
10601         return close ? close : false;
10602     },
10603     
10604     removeBtnClick : function(e, h, el)
10605     {
10606         e.preventDefault();
10607         
10608         if(this.fireEvent("remove", this) !== false){
10609             this.reset();
10610             this.fireEvent("afterremove", this)
10611         }
10612     },
10613     
10614     createList : function()
10615     {
10616         this.list = Roo.get(document.body).createChild({
10617             tag: 'ul',
10618             cls: 'typeahead typeahead-long dropdown-menu',
10619             style: 'display:none'
10620         });
10621         
10622         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10623         
10624     },
10625
10626     // private
10627     initTrigger : function(){
10628        
10629     },
10630
10631     // private
10632     onDestroy : function(){
10633         if(this.trigger){
10634             this.trigger.removeAllListeners();
10635           //  this.trigger.remove();
10636         }
10637         //if(this.wrap){
10638         //    this.wrap.remove();
10639         //}
10640         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10641     },
10642
10643     // private
10644     onFocus : function(){
10645         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10646         /*
10647         if(!this.mimicing){
10648             this.wrap.addClass('x-trigger-wrap-focus');
10649             this.mimicing = true;
10650             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10651             if(this.monitorTab){
10652                 this.el.on("keydown", this.checkTab, this);
10653             }
10654         }
10655         */
10656     },
10657
10658     // private
10659     checkTab : function(e){
10660         if(e.getKey() == e.TAB){
10661             this.triggerBlur();
10662         }
10663     },
10664
10665     // private
10666     onBlur : function(){
10667         // do nothing
10668     },
10669
10670     // private
10671     mimicBlur : function(e, t){
10672         /*
10673         if(!this.wrap.contains(t) && this.validateBlur()){
10674             this.triggerBlur();
10675         }
10676         */
10677     },
10678
10679     // private
10680     triggerBlur : function(){
10681         this.mimicing = false;
10682         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10683         if(this.monitorTab){
10684             this.el.un("keydown", this.checkTab, this);
10685         }
10686         //this.wrap.removeClass('x-trigger-wrap-focus');
10687         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10688     },
10689
10690     // private
10691     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10692     validateBlur : function(e, t){
10693         return true;
10694     },
10695
10696     // private
10697     onDisable : function(){
10698         this.inputEl().dom.disabled = true;
10699         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10700         //if(this.wrap){
10701         //    this.wrap.addClass('x-item-disabled');
10702         //}
10703     },
10704
10705     // private
10706     onEnable : function(){
10707         this.inputEl().dom.disabled = false;
10708         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10709         //if(this.wrap){
10710         //    this.el.removeClass('x-item-disabled');
10711         //}
10712     },
10713
10714     // private
10715     onShow : function(){
10716         var ae = this.getActionEl();
10717         
10718         if(ae){
10719             ae.dom.style.display = '';
10720             ae.dom.style.visibility = 'visible';
10721         }
10722     },
10723
10724     // private
10725     
10726     onHide : function(){
10727         var ae = this.getActionEl();
10728         ae.dom.style.display = 'none';
10729     },
10730
10731     /**
10732      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10733      * by an implementing function.
10734      * @method
10735      * @param {EventObject} e
10736      */
10737     onTriggerClick : Roo.emptyFn
10738 });
10739  /*
10740  * Based on:
10741  * Ext JS Library 1.1.1
10742  * Copyright(c) 2006-2007, Ext JS, LLC.
10743  *
10744  * Originally Released Under LGPL - original licence link has changed is not relivant.
10745  *
10746  * Fork - LGPL
10747  * <script type="text/javascript">
10748  */
10749
10750
10751 /**
10752  * @class Roo.data.SortTypes
10753  * @singleton
10754  * Defines the default sorting (casting?) comparison functions used when sorting data.
10755  */
10756 Roo.data.SortTypes = {
10757     /**
10758      * Default sort that does nothing
10759      * @param {Mixed} s The value being converted
10760      * @return {Mixed} The comparison value
10761      */
10762     none : function(s){
10763         return s;
10764     },
10765     
10766     /**
10767      * The regular expression used to strip tags
10768      * @type {RegExp}
10769      * @property
10770      */
10771     stripTagsRE : /<\/?[^>]+>/gi,
10772     
10773     /**
10774      * Strips all HTML tags to sort on text only
10775      * @param {Mixed} s The value being converted
10776      * @return {String} The comparison value
10777      */
10778     asText : function(s){
10779         return String(s).replace(this.stripTagsRE, "");
10780     },
10781     
10782     /**
10783      * Strips all HTML tags to sort on text only - Case insensitive
10784      * @param {Mixed} s The value being converted
10785      * @return {String} The comparison value
10786      */
10787     asUCText : function(s){
10788         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10789     },
10790     
10791     /**
10792      * Case insensitive string
10793      * @param {Mixed} s The value being converted
10794      * @return {String} The comparison value
10795      */
10796     asUCString : function(s) {
10797         return String(s).toUpperCase();
10798     },
10799     
10800     /**
10801      * Date sorting
10802      * @param {Mixed} s The value being converted
10803      * @return {Number} The comparison value
10804      */
10805     asDate : function(s) {
10806         if(!s){
10807             return 0;
10808         }
10809         if(s instanceof Date){
10810             return s.getTime();
10811         }
10812         return Date.parse(String(s));
10813     },
10814     
10815     /**
10816      * Float sorting
10817      * @param {Mixed} s The value being converted
10818      * @return {Float} The comparison value
10819      */
10820     asFloat : function(s) {
10821         var val = parseFloat(String(s).replace(/,/g, ""));
10822         if(isNaN(val)) {
10823             val = 0;
10824         }
10825         return val;
10826     },
10827     
10828     /**
10829      * Integer sorting
10830      * @param {Mixed} s The value being converted
10831      * @return {Number} The comparison value
10832      */
10833     asInt : function(s) {
10834         var val = parseInt(String(s).replace(/,/g, ""));
10835         if(isNaN(val)) {
10836             val = 0;
10837         }
10838         return val;
10839     }
10840 };/*
10841  * Based on:
10842  * Ext JS Library 1.1.1
10843  * Copyright(c) 2006-2007, Ext JS, LLC.
10844  *
10845  * Originally Released Under LGPL - original licence link has changed is not relivant.
10846  *
10847  * Fork - LGPL
10848  * <script type="text/javascript">
10849  */
10850
10851 /**
10852 * @class Roo.data.Record
10853  * Instances of this class encapsulate both record <em>definition</em> information, and record
10854  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10855  * to access Records cached in an {@link Roo.data.Store} object.<br>
10856  * <p>
10857  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10858  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10859  * objects.<br>
10860  * <p>
10861  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10862  * @constructor
10863  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10864  * {@link #create}. The parameters are the same.
10865  * @param {Array} data An associative Array of data values keyed by the field name.
10866  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10867  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10868  * not specified an integer id is generated.
10869  */
10870 Roo.data.Record = function(data, id){
10871     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10872     this.data = data;
10873 };
10874
10875 /**
10876  * Generate a constructor for a specific record layout.
10877  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10878  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10879  * Each field definition object may contain the following properties: <ul>
10880  * <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,
10881  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10882  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10883  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10884  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10885  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10886  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10887  * this may be omitted.</p></li>
10888  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10889  * <ul><li>auto (Default, implies no conversion)</li>
10890  * <li>string</li>
10891  * <li>int</li>
10892  * <li>float</li>
10893  * <li>boolean</li>
10894  * <li>date</li></ul></p></li>
10895  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10896  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10897  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10898  * by the Reader into an object that will be stored in the Record. It is passed the
10899  * following parameters:<ul>
10900  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10901  * </ul></p></li>
10902  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10903  * </ul>
10904  * <br>usage:<br><pre><code>
10905 var TopicRecord = Roo.data.Record.create(
10906     {name: 'title', mapping: 'topic_title'},
10907     {name: 'author', mapping: 'username'},
10908     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10909     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10910     {name: 'lastPoster', mapping: 'user2'},
10911     {name: 'excerpt', mapping: 'post_text'}
10912 );
10913
10914 var myNewRecord = new TopicRecord({
10915     title: 'Do my job please',
10916     author: 'noobie',
10917     totalPosts: 1,
10918     lastPost: new Date(),
10919     lastPoster: 'Animal',
10920     excerpt: 'No way dude!'
10921 });
10922 myStore.add(myNewRecord);
10923 </code></pre>
10924  * @method create
10925  * @static
10926  */
10927 Roo.data.Record.create = function(o){
10928     var f = function(){
10929         f.superclass.constructor.apply(this, arguments);
10930     };
10931     Roo.extend(f, Roo.data.Record);
10932     var p = f.prototype;
10933     p.fields = new Roo.util.MixedCollection(false, function(field){
10934         return field.name;
10935     });
10936     for(var i = 0, len = o.length; i < len; i++){
10937         p.fields.add(new Roo.data.Field(o[i]));
10938     }
10939     f.getField = function(name){
10940         return p.fields.get(name);  
10941     };
10942     return f;
10943 };
10944
10945 Roo.data.Record.AUTO_ID = 1000;
10946 Roo.data.Record.EDIT = 'edit';
10947 Roo.data.Record.REJECT = 'reject';
10948 Roo.data.Record.COMMIT = 'commit';
10949
10950 Roo.data.Record.prototype = {
10951     /**
10952      * Readonly flag - true if this record has been modified.
10953      * @type Boolean
10954      */
10955     dirty : false,
10956     editing : false,
10957     error: null,
10958     modified: null,
10959
10960     // private
10961     join : function(store){
10962         this.store = store;
10963     },
10964
10965     /**
10966      * Set the named field to the specified value.
10967      * @param {String} name The name of the field to set.
10968      * @param {Object} value The value to set the field to.
10969      */
10970     set : function(name, value){
10971         if(this.data[name] == value){
10972             return;
10973         }
10974         this.dirty = true;
10975         if(!this.modified){
10976             this.modified = {};
10977         }
10978         if(typeof this.modified[name] == 'undefined'){
10979             this.modified[name] = this.data[name];
10980         }
10981         this.data[name] = value;
10982         if(!this.editing && this.store){
10983             this.store.afterEdit(this);
10984         }       
10985     },
10986
10987     /**
10988      * Get the value of the named field.
10989      * @param {String} name The name of the field to get the value of.
10990      * @return {Object} The value of the field.
10991      */
10992     get : function(name){
10993         return this.data[name]; 
10994     },
10995
10996     // private
10997     beginEdit : function(){
10998         this.editing = true;
10999         this.modified = {}; 
11000     },
11001
11002     // private
11003     cancelEdit : function(){
11004         this.editing = false;
11005         delete this.modified;
11006     },
11007
11008     // private
11009     endEdit : function(){
11010         this.editing = false;
11011         if(this.dirty && this.store){
11012             this.store.afterEdit(this);
11013         }
11014     },
11015
11016     /**
11017      * Usually called by the {@link Roo.data.Store} which owns the Record.
11018      * Rejects all changes made to the Record since either creation, or the last commit operation.
11019      * Modified fields are reverted to their original values.
11020      * <p>
11021      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11022      * of reject operations.
11023      */
11024     reject : function(){
11025         var m = this.modified;
11026         for(var n in m){
11027             if(typeof m[n] != "function"){
11028                 this.data[n] = m[n];
11029             }
11030         }
11031         this.dirty = false;
11032         delete this.modified;
11033         this.editing = false;
11034         if(this.store){
11035             this.store.afterReject(this);
11036         }
11037     },
11038
11039     /**
11040      * Usually called by the {@link Roo.data.Store} which owns the Record.
11041      * Commits all changes made to the Record since either creation, or the last commit operation.
11042      * <p>
11043      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11044      * of commit operations.
11045      */
11046     commit : function(){
11047         this.dirty = false;
11048         delete this.modified;
11049         this.editing = false;
11050         if(this.store){
11051             this.store.afterCommit(this);
11052         }
11053     },
11054
11055     // private
11056     hasError : function(){
11057         return this.error != null;
11058     },
11059
11060     // private
11061     clearError : function(){
11062         this.error = null;
11063     },
11064
11065     /**
11066      * Creates a copy of this record.
11067      * @param {String} id (optional) A new record id if you don't want to use this record's id
11068      * @return {Record}
11069      */
11070     copy : function(newId) {
11071         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11072     }
11073 };/*
11074  * Based on:
11075  * Ext JS Library 1.1.1
11076  * Copyright(c) 2006-2007, Ext JS, LLC.
11077  *
11078  * Originally Released Under LGPL - original licence link has changed is not relivant.
11079  *
11080  * Fork - LGPL
11081  * <script type="text/javascript">
11082  */
11083
11084
11085
11086 /**
11087  * @class Roo.data.Store
11088  * @extends Roo.util.Observable
11089  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11090  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11091  * <p>
11092  * 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
11093  * has no knowledge of the format of the data returned by the Proxy.<br>
11094  * <p>
11095  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11096  * instances from the data object. These records are cached and made available through accessor functions.
11097  * @constructor
11098  * Creates a new Store.
11099  * @param {Object} config A config object containing the objects needed for the Store to access data,
11100  * and read the data into Records.
11101  */
11102 Roo.data.Store = function(config){
11103     this.data = new Roo.util.MixedCollection(false);
11104     this.data.getKey = function(o){
11105         return o.id;
11106     };
11107     this.baseParams = {};
11108     // private
11109     this.paramNames = {
11110         "start" : "start",
11111         "limit" : "limit",
11112         "sort" : "sort",
11113         "dir" : "dir",
11114         "multisort" : "_multisort"
11115     };
11116
11117     if(config && config.data){
11118         this.inlineData = config.data;
11119         delete config.data;
11120     }
11121
11122     Roo.apply(this, config);
11123     
11124     if(this.reader){ // reader passed
11125         this.reader = Roo.factory(this.reader, Roo.data);
11126         this.reader.xmodule = this.xmodule || false;
11127         if(!this.recordType){
11128             this.recordType = this.reader.recordType;
11129         }
11130         if(this.reader.onMetaChange){
11131             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11132         }
11133     }
11134
11135     if(this.recordType){
11136         this.fields = this.recordType.prototype.fields;
11137     }
11138     this.modified = [];
11139
11140     this.addEvents({
11141         /**
11142          * @event datachanged
11143          * Fires when the data cache has changed, and a widget which is using this Store
11144          * as a Record cache should refresh its view.
11145          * @param {Store} this
11146          */
11147         datachanged : true,
11148         /**
11149          * @event metachange
11150          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11151          * @param {Store} this
11152          * @param {Object} meta The JSON metadata
11153          */
11154         metachange : true,
11155         /**
11156          * @event add
11157          * Fires when Records have been added to the Store
11158          * @param {Store} this
11159          * @param {Roo.data.Record[]} records The array of Records added
11160          * @param {Number} index The index at which the record(s) were added
11161          */
11162         add : true,
11163         /**
11164          * @event remove
11165          * Fires when a Record has been removed from the Store
11166          * @param {Store} this
11167          * @param {Roo.data.Record} record The Record that was removed
11168          * @param {Number} index The index at which the record was removed
11169          */
11170         remove : true,
11171         /**
11172          * @event update
11173          * Fires when a Record has been updated
11174          * @param {Store} this
11175          * @param {Roo.data.Record} record The Record that was updated
11176          * @param {String} operation The update operation being performed.  Value may be one of:
11177          * <pre><code>
11178  Roo.data.Record.EDIT
11179  Roo.data.Record.REJECT
11180  Roo.data.Record.COMMIT
11181          * </code></pre>
11182          */
11183         update : true,
11184         /**
11185          * @event clear
11186          * Fires when the data cache has been cleared.
11187          * @param {Store} this
11188          */
11189         clear : true,
11190         /**
11191          * @event beforeload
11192          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11193          * the load action will be canceled.
11194          * @param {Store} this
11195          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11196          */
11197         beforeload : true,
11198         /**
11199          * @event beforeloadadd
11200          * Fires after a new set of Records has been loaded.
11201          * @param {Store} this
11202          * @param {Roo.data.Record[]} records The Records that were loaded
11203          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11204          */
11205         beforeloadadd : true,
11206         /**
11207          * @event load
11208          * Fires after a new set of Records has been loaded, before they are added to the store.
11209          * @param {Store} this
11210          * @param {Roo.data.Record[]} records The Records that were loaded
11211          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11212          * @params {Object} return from reader
11213          */
11214         load : true,
11215         /**
11216          * @event loadexception
11217          * Fires if an exception occurs in the Proxy during loading.
11218          * Called with the signature of the Proxy's "loadexception" event.
11219          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11220          * 
11221          * @param {Proxy} 
11222          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11223          * @param {Object} load options 
11224          * @param {Object} jsonData from your request (normally this contains the Exception)
11225          */
11226         loadexception : true
11227     });
11228     
11229     if(this.proxy){
11230         this.proxy = Roo.factory(this.proxy, Roo.data);
11231         this.proxy.xmodule = this.xmodule || false;
11232         this.relayEvents(this.proxy,  ["loadexception"]);
11233     }
11234     this.sortToggle = {};
11235     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11236
11237     Roo.data.Store.superclass.constructor.call(this);
11238
11239     if(this.inlineData){
11240         this.loadData(this.inlineData);
11241         delete this.inlineData;
11242     }
11243 };
11244
11245 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11246      /**
11247     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11248     * without a remote query - used by combo/forms at present.
11249     */
11250     
11251     /**
11252     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11253     */
11254     /**
11255     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11256     */
11257     /**
11258     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11259     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11260     */
11261     /**
11262     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11263     * on any HTTP request
11264     */
11265     /**
11266     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11267     */
11268     /**
11269     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11270     */
11271     multiSort: false,
11272     /**
11273     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11274     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11275     */
11276     remoteSort : false,
11277
11278     /**
11279     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11280      * loaded or when a record is removed. (defaults to false).
11281     */
11282     pruneModifiedRecords : false,
11283
11284     // private
11285     lastOptions : null,
11286
11287     /**
11288      * Add Records to the Store and fires the add event.
11289      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11290      */
11291     add : function(records){
11292         records = [].concat(records);
11293         for(var i = 0, len = records.length; i < len; i++){
11294             records[i].join(this);
11295         }
11296         var index = this.data.length;
11297         this.data.addAll(records);
11298         this.fireEvent("add", this, records, index);
11299     },
11300
11301     /**
11302      * Remove a Record from the Store and fires the remove event.
11303      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11304      */
11305     remove : function(record){
11306         var index = this.data.indexOf(record);
11307         this.data.removeAt(index);
11308  
11309         if(this.pruneModifiedRecords){
11310             this.modified.remove(record);
11311         }
11312         this.fireEvent("remove", this, record, index);
11313     },
11314
11315     /**
11316      * Remove all Records from the Store and fires the clear event.
11317      */
11318     removeAll : function(){
11319         this.data.clear();
11320         if(this.pruneModifiedRecords){
11321             this.modified = [];
11322         }
11323         this.fireEvent("clear", this);
11324     },
11325
11326     /**
11327      * Inserts Records to the Store at the given index and fires the add event.
11328      * @param {Number} index The start index at which to insert the passed Records.
11329      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11330      */
11331     insert : function(index, records){
11332         records = [].concat(records);
11333         for(var i = 0, len = records.length; i < len; i++){
11334             this.data.insert(index, records[i]);
11335             records[i].join(this);
11336         }
11337         this.fireEvent("add", this, records, index);
11338     },
11339
11340     /**
11341      * Get the index within the cache of the passed Record.
11342      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11343      * @return {Number} The index of the passed Record. Returns -1 if not found.
11344      */
11345     indexOf : function(record){
11346         return this.data.indexOf(record);
11347     },
11348
11349     /**
11350      * Get the index within the cache of the Record with the passed id.
11351      * @param {String} id The id of the Record to find.
11352      * @return {Number} The index of the Record. Returns -1 if not found.
11353      */
11354     indexOfId : function(id){
11355         return this.data.indexOfKey(id);
11356     },
11357
11358     /**
11359      * Get the Record with the specified id.
11360      * @param {String} id The id of the Record to find.
11361      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11362      */
11363     getById : function(id){
11364         return this.data.key(id);
11365     },
11366
11367     /**
11368      * Get the Record at the specified index.
11369      * @param {Number} index The index of the Record to find.
11370      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11371      */
11372     getAt : function(index){
11373         return this.data.itemAt(index);
11374     },
11375
11376     /**
11377      * Returns a range of Records between specified indices.
11378      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11379      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11380      * @return {Roo.data.Record[]} An array of Records
11381      */
11382     getRange : function(start, end){
11383         return this.data.getRange(start, end);
11384     },
11385
11386     // private
11387     storeOptions : function(o){
11388         o = Roo.apply({}, o);
11389         delete o.callback;
11390         delete o.scope;
11391         this.lastOptions = o;
11392     },
11393
11394     /**
11395      * Loads the Record cache from the configured Proxy using the configured Reader.
11396      * <p>
11397      * If using remote paging, then the first load call must specify the <em>start</em>
11398      * and <em>limit</em> properties in the options.params property to establish the initial
11399      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11400      * <p>
11401      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11402      * and this call will return before the new data has been loaded. Perform any post-processing
11403      * in a callback function, or in a "load" event handler.</strong>
11404      * <p>
11405      * @param {Object} options An object containing properties which control loading options:<ul>
11406      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11407      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11408      * passed the following arguments:<ul>
11409      * <li>r : Roo.data.Record[]</li>
11410      * <li>options: Options object from the load call</li>
11411      * <li>success: Boolean success indicator</li></ul></li>
11412      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11413      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11414      * </ul>
11415      */
11416     load : function(options){
11417         options = options || {};
11418         if(this.fireEvent("beforeload", this, options) !== false){
11419             this.storeOptions(options);
11420             var p = Roo.apply(options.params || {}, this.baseParams);
11421             // if meta was not loaded from remote source.. try requesting it.
11422             if (!this.reader.metaFromRemote) {
11423                 p._requestMeta = 1;
11424             }
11425             if(this.sortInfo && this.remoteSort){
11426                 var pn = this.paramNames;
11427                 p[pn["sort"]] = this.sortInfo.field;
11428                 p[pn["dir"]] = this.sortInfo.direction;
11429             }
11430             if (this.multiSort) {
11431                 var pn = this.paramNames;
11432                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11433             }
11434             
11435             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11436         }
11437     },
11438
11439     /**
11440      * Reloads the Record cache from the configured Proxy using the configured Reader and
11441      * the options from the last load operation performed.
11442      * @param {Object} options (optional) An object containing properties which may override the options
11443      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11444      * the most recently used options are reused).
11445      */
11446     reload : function(options){
11447         this.load(Roo.applyIf(options||{}, this.lastOptions));
11448     },
11449
11450     // private
11451     // Called as a callback by the Reader during a load operation.
11452     loadRecords : function(o, options, success){
11453         if(!o || success === false){
11454             if(success !== false){
11455                 this.fireEvent("load", this, [], options, o);
11456             }
11457             if(options.callback){
11458                 options.callback.call(options.scope || this, [], options, false);
11459             }
11460             return;
11461         }
11462         // if data returned failure - throw an exception.
11463         if (o.success === false) {
11464             // show a message if no listener is registered.
11465             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11466                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11467             }
11468             // loadmask wil be hooked into this..
11469             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11470             return;
11471         }
11472         var r = o.records, t = o.totalRecords || r.length;
11473         
11474         this.fireEvent("beforeloadadd", this, r, options, o);
11475         
11476         if(!options || options.add !== true){
11477             if(this.pruneModifiedRecords){
11478                 this.modified = [];
11479             }
11480             for(var i = 0, len = r.length; i < len; i++){
11481                 r[i].join(this);
11482             }
11483             if(this.snapshot){
11484                 this.data = this.snapshot;
11485                 delete this.snapshot;
11486             }
11487             this.data.clear();
11488             this.data.addAll(r);
11489             this.totalLength = t;
11490             this.applySort();
11491             this.fireEvent("datachanged", this);
11492         }else{
11493             this.totalLength = Math.max(t, this.data.length+r.length);
11494             this.add(r);
11495         }
11496         
11497         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11498                 
11499             var e = new Roo.data.Record({});
11500
11501             e.set(this.parent.displayField, this.parent.emptyTitle);
11502             e.set(this.parent.valueField, '');
11503
11504             this.insert(0, e);
11505         }
11506             
11507         this.fireEvent("load", this, r, options, o);
11508         if(options.callback){
11509             options.callback.call(options.scope || this, r, options, true);
11510         }
11511     },
11512
11513
11514     /**
11515      * Loads data from a passed data block. A Reader which understands the format of the data
11516      * must have been configured in the constructor.
11517      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11518      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11519      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11520      */
11521     loadData : function(o, append){
11522         var r = this.reader.readRecords(o);
11523         this.loadRecords(r, {add: append}, true);
11524     },
11525
11526     /**
11527      * Gets the number of cached records.
11528      * <p>
11529      * <em>If using paging, this may not be the total size of the dataset. If the data object
11530      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11531      * the data set size</em>
11532      */
11533     getCount : function(){
11534         return this.data.length || 0;
11535     },
11536
11537     /**
11538      * Gets the total number of records in the dataset as returned by the server.
11539      * <p>
11540      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11541      * the dataset size</em>
11542      */
11543     getTotalCount : function(){
11544         return this.totalLength || 0;
11545     },
11546
11547     /**
11548      * Returns the sort state of the Store as an object with two properties:
11549      * <pre><code>
11550  field {String} The name of the field by which the Records are sorted
11551  direction {String} The sort order, "ASC" or "DESC"
11552      * </code></pre>
11553      */
11554     getSortState : function(){
11555         return this.sortInfo;
11556     },
11557
11558     // private
11559     applySort : function(){
11560         if(this.sortInfo && !this.remoteSort){
11561             var s = this.sortInfo, f = s.field;
11562             var st = this.fields.get(f).sortType;
11563             var fn = function(r1, r2){
11564                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11565                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11566             };
11567             this.data.sort(s.direction, fn);
11568             if(this.snapshot && this.snapshot != this.data){
11569                 this.snapshot.sort(s.direction, fn);
11570             }
11571         }
11572     },
11573
11574     /**
11575      * Sets the default sort column and order to be used by the next load operation.
11576      * @param {String} fieldName The name of the field to sort by.
11577      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11578      */
11579     setDefaultSort : function(field, dir){
11580         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11581     },
11582
11583     /**
11584      * Sort the Records.
11585      * If remote sorting is used, the sort is performed on the server, and the cache is
11586      * reloaded. If local sorting is used, the cache is sorted internally.
11587      * @param {String} fieldName The name of the field to sort by.
11588      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11589      */
11590     sort : function(fieldName, dir){
11591         var f = this.fields.get(fieldName);
11592         if(!dir){
11593             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11594             
11595             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11596                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11597             }else{
11598                 dir = f.sortDir;
11599             }
11600         }
11601         this.sortToggle[f.name] = dir;
11602         this.sortInfo = {field: f.name, direction: dir};
11603         if(!this.remoteSort){
11604             this.applySort();
11605             this.fireEvent("datachanged", this);
11606         }else{
11607             this.load(this.lastOptions);
11608         }
11609     },
11610
11611     /**
11612      * Calls the specified function for each of the Records in the cache.
11613      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11614      * Returning <em>false</em> aborts and exits the iteration.
11615      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11616      */
11617     each : function(fn, scope){
11618         this.data.each(fn, scope);
11619     },
11620
11621     /**
11622      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11623      * (e.g., during paging).
11624      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11625      */
11626     getModifiedRecords : function(){
11627         return this.modified;
11628     },
11629
11630     // private
11631     createFilterFn : function(property, value, anyMatch){
11632         if(!value.exec){ // not a regex
11633             value = String(value);
11634             if(value.length == 0){
11635                 return false;
11636             }
11637             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11638         }
11639         return function(r){
11640             return value.test(r.data[property]);
11641         };
11642     },
11643
11644     /**
11645      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11646      * @param {String} property A field on your records
11647      * @param {Number} start The record index to start at (defaults to 0)
11648      * @param {Number} end The last record index to include (defaults to length - 1)
11649      * @return {Number} The sum
11650      */
11651     sum : function(property, start, end){
11652         var rs = this.data.items, v = 0;
11653         start = start || 0;
11654         end = (end || end === 0) ? end : rs.length-1;
11655
11656         for(var i = start; i <= end; i++){
11657             v += (rs[i].data[property] || 0);
11658         }
11659         return v;
11660     },
11661
11662     /**
11663      * Filter the records by a specified property.
11664      * @param {String} field A field on your records
11665      * @param {String/RegExp} value Either a string that the field
11666      * should start with or a RegExp to test against the field
11667      * @param {Boolean} anyMatch True to match any part not just the beginning
11668      */
11669     filter : function(property, value, anyMatch){
11670         var fn = this.createFilterFn(property, value, anyMatch);
11671         return fn ? this.filterBy(fn) : this.clearFilter();
11672     },
11673
11674     /**
11675      * Filter by a function. The specified function will be called with each
11676      * record in this data source. If the function returns true the record is included,
11677      * otherwise it is filtered.
11678      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11679      * @param {Object} scope (optional) The scope of the function (defaults to this)
11680      */
11681     filterBy : function(fn, scope){
11682         this.snapshot = this.snapshot || this.data;
11683         this.data = this.queryBy(fn, scope||this);
11684         this.fireEvent("datachanged", this);
11685     },
11686
11687     /**
11688      * Query the records by a specified property.
11689      * @param {String} field A field on your records
11690      * @param {String/RegExp} value Either a string that the field
11691      * should start with or a RegExp to test against the field
11692      * @param {Boolean} anyMatch True to match any part not just the beginning
11693      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11694      */
11695     query : function(property, value, anyMatch){
11696         var fn = this.createFilterFn(property, value, anyMatch);
11697         return fn ? this.queryBy(fn) : this.data.clone();
11698     },
11699
11700     /**
11701      * Query by a function. The specified function will be called with each
11702      * record in this data source. If the function returns true the record is included
11703      * in the results.
11704      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11705      * @param {Object} scope (optional) The scope of the function (defaults to this)
11706       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11707      **/
11708     queryBy : function(fn, scope){
11709         var data = this.snapshot || this.data;
11710         return data.filterBy(fn, scope||this);
11711     },
11712
11713     /**
11714      * Collects unique values for a particular dataIndex from this store.
11715      * @param {String} dataIndex The property to collect
11716      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11717      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11718      * @return {Array} An array of the unique values
11719      **/
11720     collect : function(dataIndex, allowNull, bypassFilter){
11721         var d = (bypassFilter === true && this.snapshot) ?
11722                 this.snapshot.items : this.data.items;
11723         var v, sv, r = [], l = {};
11724         for(var i = 0, len = d.length; i < len; i++){
11725             v = d[i].data[dataIndex];
11726             sv = String(v);
11727             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11728                 l[sv] = true;
11729                 r[r.length] = v;
11730             }
11731         }
11732         return r;
11733     },
11734
11735     /**
11736      * Revert to a view of the Record cache with no filtering applied.
11737      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11738      */
11739     clearFilter : function(suppressEvent){
11740         if(this.snapshot && this.snapshot != this.data){
11741             this.data = this.snapshot;
11742             delete this.snapshot;
11743             if(suppressEvent !== true){
11744                 this.fireEvent("datachanged", this);
11745             }
11746         }
11747     },
11748
11749     // private
11750     afterEdit : function(record){
11751         if(this.modified.indexOf(record) == -1){
11752             this.modified.push(record);
11753         }
11754         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11755     },
11756     
11757     // private
11758     afterReject : function(record){
11759         this.modified.remove(record);
11760         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11761     },
11762
11763     // private
11764     afterCommit : function(record){
11765         this.modified.remove(record);
11766         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11767     },
11768
11769     /**
11770      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11771      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11772      */
11773     commitChanges : function(){
11774         var m = this.modified.slice(0);
11775         this.modified = [];
11776         for(var i = 0, len = m.length; i < len; i++){
11777             m[i].commit();
11778         }
11779     },
11780
11781     /**
11782      * Cancel outstanding changes on all changed records.
11783      */
11784     rejectChanges : function(){
11785         var m = this.modified.slice(0);
11786         this.modified = [];
11787         for(var i = 0, len = m.length; i < len; i++){
11788             m[i].reject();
11789         }
11790     },
11791
11792     onMetaChange : function(meta, rtype, o){
11793         this.recordType = rtype;
11794         this.fields = rtype.prototype.fields;
11795         delete this.snapshot;
11796         this.sortInfo = meta.sortInfo || this.sortInfo;
11797         this.modified = [];
11798         this.fireEvent('metachange', this, this.reader.meta);
11799     },
11800     
11801     moveIndex : function(data, type)
11802     {
11803         var index = this.indexOf(data);
11804         
11805         var newIndex = index + type;
11806         
11807         this.remove(data);
11808         
11809         this.insert(newIndex, data);
11810         
11811     }
11812 });/*
11813  * Based on:
11814  * Ext JS Library 1.1.1
11815  * Copyright(c) 2006-2007, Ext JS, LLC.
11816  *
11817  * Originally Released Under LGPL - original licence link has changed is not relivant.
11818  *
11819  * Fork - LGPL
11820  * <script type="text/javascript">
11821  */
11822
11823 /**
11824  * @class Roo.data.SimpleStore
11825  * @extends Roo.data.Store
11826  * Small helper class to make creating Stores from Array data easier.
11827  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11828  * @cfg {Array} fields An array of field definition objects, or field name strings.
11829  * @cfg {Array} data The multi-dimensional array of data
11830  * @constructor
11831  * @param {Object} config
11832  */
11833 Roo.data.SimpleStore = function(config){
11834     Roo.data.SimpleStore.superclass.constructor.call(this, {
11835         isLocal : true,
11836         reader: new Roo.data.ArrayReader({
11837                 id: config.id
11838             },
11839             Roo.data.Record.create(config.fields)
11840         ),
11841         proxy : new Roo.data.MemoryProxy(config.data)
11842     });
11843     this.load();
11844 };
11845 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11846  * Based on:
11847  * Ext JS Library 1.1.1
11848  * Copyright(c) 2006-2007, Ext JS, LLC.
11849  *
11850  * Originally Released Under LGPL - original licence link has changed is not relivant.
11851  *
11852  * Fork - LGPL
11853  * <script type="text/javascript">
11854  */
11855
11856 /**
11857 /**
11858  * @extends Roo.data.Store
11859  * @class Roo.data.JsonStore
11860  * Small helper class to make creating Stores for JSON data easier. <br/>
11861 <pre><code>
11862 var store = new Roo.data.JsonStore({
11863     url: 'get-images.php',
11864     root: 'images',
11865     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11866 });
11867 </code></pre>
11868  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11869  * JsonReader and HttpProxy (unless inline data is provided).</b>
11870  * @cfg {Array} fields An array of field definition objects, or field name strings.
11871  * @constructor
11872  * @param {Object} config
11873  */
11874 Roo.data.JsonStore = function(c){
11875     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11876         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11877         reader: new Roo.data.JsonReader(c, c.fields)
11878     }));
11879 };
11880 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11881  * Based on:
11882  * Ext JS Library 1.1.1
11883  * Copyright(c) 2006-2007, Ext JS, LLC.
11884  *
11885  * Originally Released Under LGPL - original licence link has changed is not relivant.
11886  *
11887  * Fork - LGPL
11888  * <script type="text/javascript">
11889  */
11890
11891  
11892 Roo.data.Field = function(config){
11893     if(typeof config == "string"){
11894         config = {name: config};
11895     }
11896     Roo.apply(this, config);
11897     
11898     if(!this.type){
11899         this.type = "auto";
11900     }
11901     
11902     var st = Roo.data.SortTypes;
11903     // named sortTypes are supported, here we look them up
11904     if(typeof this.sortType == "string"){
11905         this.sortType = st[this.sortType];
11906     }
11907     
11908     // set default sortType for strings and dates
11909     if(!this.sortType){
11910         switch(this.type){
11911             case "string":
11912                 this.sortType = st.asUCString;
11913                 break;
11914             case "date":
11915                 this.sortType = st.asDate;
11916                 break;
11917             default:
11918                 this.sortType = st.none;
11919         }
11920     }
11921
11922     // define once
11923     var stripRe = /[\$,%]/g;
11924
11925     // prebuilt conversion function for this field, instead of
11926     // switching every time we're reading a value
11927     if(!this.convert){
11928         var cv, dateFormat = this.dateFormat;
11929         switch(this.type){
11930             case "":
11931             case "auto":
11932             case undefined:
11933                 cv = function(v){ return v; };
11934                 break;
11935             case "string":
11936                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11937                 break;
11938             case "int":
11939                 cv = function(v){
11940                     return v !== undefined && v !== null && v !== '' ?
11941                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11942                     };
11943                 break;
11944             case "float":
11945                 cv = function(v){
11946                     return v !== undefined && v !== null && v !== '' ?
11947                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11948                     };
11949                 break;
11950             case "bool":
11951             case "boolean":
11952                 cv = function(v){ return v === true || v === "true" || v == 1; };
11953                 break;
11954             case "date":
11955                 cv = function(v){
11956                     if(!v){
11957                         return '';
11958                     }
11959                     if(v instanceof Date){
11960                         return v;
11961                     }
11962                     if(dateFormat){
11963                         if(dateFormat == "timestamp"){
11964                             return new Date(v*1000);
11965                         }
11966                         return Date.parseDate(v, dateFormat);
11967                     }
11968                     var parsed = Date.parse(v);
11969                     return parsed ? new Date(parsed) : null;
11970                 };
11971              break;
11972             
11973         }
11974         this.convert = cv;
11975     }
11976 };
11977
11978 Roo.data.Field.prototype = {
11979     dateFormat: null,
11980     defaultValue: "",
11981     mapping: null,
11982     sortType : null,
11983     sortDir : "ASC"
11984 };/*
11985  * Based on:
11986  * Ext JS Library 1.1.1
11987  * Copyright(c) 2006-2007, Ext JS, LLC.
11988  *
11989  * Originally Released Under LGPL - original licence link has changed is not relivant.
11990  *
11991  * Fork - LGPL
11992  * <script type="text/javascript">
11993  */
11994  
11995 // Base class for reading structured data from a data source.  This class is intended to be
11996 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11997
11998 /**
11999  * @class Roo.data.DataReader
12000  * Base class for reading structured data from a data source.  This class is intended to be
12001  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12002  */
12003
12004 Roo.data.DataReader = function(meta, recordType){
12005     
12006     this.meta = meta;
12007     
12008     this.recordType = recordType instanceof Array ? 
12009         Roo.data.Record.create(recordType) : recordType;
12010 };
12011
12012 Roo.data.DataReader.prototype = {
12013      /**
12014      * Create an empty record
12015      * @param {Object} data (optional) - overlay some values
12016      * @return {Roo.data.Record} record created.
12017      */
12018     newRow :  function(d) {
12019         var da =  {};
12020         this.recordType.prototype.fields.each(function(c) {
12021             switch( c.type) {
12022                 case 'int' : da[c.name] = 0; break;
12023                 case 'date' : da[c.name] = new Date(); break;
12024                 case 'float' : da[c.name] = 0.0; break;
12025                 case 'boolean' : da[c.name] = false; break;
12026                 default : da[c.name] = ""; break;
12027             }
12028             
12029         });
12030         return new this.recordType(Roo.apply(da, d));
12031     }
12032     
12033 };/*
12034  * Based on:
12035  * Ext JS Library 1.1.1
12036  * Copyright(c) 2006-2007, Ext JS, LLC.
12037  *
12038  * Originally Released Under LGPL - original licence link has changed is not relivant.
12039  *
12040  * Fork - LGPL
12041  * <script type="text/javascript">
12042  */
12043
12044 /**
12045  * @class Roo.data.DataProxy
12046  * @extends Roo.data.Observable
12047  * This class is an abstract base class for implementations which provide retrieval of
12048  * unformatted data objects.<br>
12049  * <p>
12050  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12051  * (of the appropriate type which knows how to parse the data object) to provide a block of
12052  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12053  * <p>
12054  * Custom implementations must implement the load method as described in
12055  * {@link Roo.data.HttpProxy#load}.
12056  */
12057 Roo.data.DataProxy = function(){
12058     this.addEvents({
12059         /**
12060          * @event beforeload
12061          * Fires before a network request is made to retrieve a data object.
12062          * @param {Object} This DataProxy object.
12063          * @param {Object} params The params parameter to the load function.
12064          */
12065         beforeload : true,
12066         /**
12067          * @event load
12068          * Fires before the load method's callback is called.
12069          * @param {Object} This DataProxy object.
12070          * @param {Object} o The data object.
12071          * @param {Object} arg The callback argument object passed to the load function.
12072          */
12073         load : true,
12074         /**
12075          * @event loadexception
12076          * Fires if an Exception occurs during data retrieval.
12077          * @param {Object} This DataProxy object.
12078          * @param {Object} o The data object.
12079          * @param {Object} arg The callback argument object passed to the load function.
12080          * @param {Object} e The Exception.
12081          */
12082         loadexception : true
12083     });
12084     Roo.data.DataProxy.superclass.constructor.call(this);
12085 };
12086
12087 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12088
12089     /**
12090      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12091      */
12092 /*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102 /**
12103  * @class Roo.data.MemoryProxy
12104  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12105  * to the Reader when its load method is called.
12106  * @constructor
12107  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12108  */
12109 Roo.data.MemoryProxy = function(data){
12110     if (data.data) {
12111         data = data.data;
12112     }
12113     Roo.data.MemoryProxy.superclass.constructor.call(this);
12114     this.data = data;
12115 };
12116
12117 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12118     
12119     /**
12120      * Load data from the requested source (in this case an in-memory
12121      * data object passed to the constructor), read the data object into
12122      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12123      * process that block using the passed callback.
12124      * @param {Object} params This parameter is not used by the MemoryProxy class.
12125      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12126      * object into a block of Roo.data.Records.
12127      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12128      * The function must be passed <ul>
12129      * <li>The Record block object</li>
12130      * <li>The "arg" argument from the load function</li>
12131      * <li>A boolean success indicator</li>
12132      * </ul>
12133      * @param {Object} scope The scope in which to call the callback
12134      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12135      */
12136     load : function(params, reader, callback, scope, arg){
12137         params = params || {};
12138         var result;
12139         try {
12140             result = reader.readRecords(this.data);
12141         }catch(e){
12142             this.fireEvent("loadexception", this, arg, null, e);
12143             callback.call(scope, null, arg, false);
12144             return;
12145         }
12146         callback.call(scope, result, arg, true);
12147     },
12148     
12149     // private
12150     update : function(params, records){
12151         
12152     }
12153 });/*
12154  * Based on:
12155  * Ext JS Library 1.1.1
12156  * Copyright(c) 2006-2007, Ext JS, LLC.
12157  *
12158  * Originally Released Under LGPL - original licence link has changed is not relivant.
12159  *
12160  * Fork - LGPL
12161  * <script type="text/javascript">
12162  */
12163 /**
12164  * @class Roo.data.HttpProxy
12165  * @extends Roo.data.DataProxy
12166  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12167  * configured to reference a certain URL.<br><br>
12168  * <p>
12169  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12170  * from which the running page was served.<br><br>
12171  * <p>
12172  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12173  * <p>
12174  * Be aware that to enable the browser to parse an XML document, the server must set
12175  * the Content-Type header in the HTTP response to "text/xml".
12176  * @constructor
12177  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12178  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12179  * will be used to make the request.
12180  */
12181 Roo.data.HttpProxy = function(conn){
12182     Roo.data.HttpProxy.superclass.constructor.call(this);
12183     // is conn a conn config or a real conn?
12184     this.conn = conn;
12185     this.useAjax = !conn || !conn.events;
12186   
12187 };
12188
12189 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12190     // thse are take from connection...
12191     
12192     /**
12193      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12194      */
12195     /**
12196      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12197      * extra parameters to each request made by this object. (defaults to undefined)
12198      */
12199     /**
12200      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12201      *  to each request made by this object. (defaults to undefined)
12202      */
12203     /**
12204      * @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)
12205      */
12206     /**
12207      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12208      */
12209      /**
12210      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12211      * @type Boolean
12212      */
12213   
12214
12215     /**
12216      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12217      * @type Boolean
12218      */
12219     /**
12220      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12221      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12222      * a finer-grained basis than the DataProxy events.
12223      */
12224     getConnection : function(){
12225         return this.useAjax ? Roo.Ajax : this.conn;
12226     },
12227
12228     /**
12229      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12230      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12231      * process that block using the passed callback.
12232      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12233      * for the request to the remote server.
12234      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12235      * object into a block of Roo.data.Records.
12236      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12237      * The function must be passed <ul>
12238      * <li>The Record block object</li>
12239      * <li>The "arg" argument from the load function</li>
12240      * <li>A boolean success indicator</li>
12241      * </ul>
12242      * @param {Object} scope The scope in which to call the callback
12243      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12244      */
12245     load : function(params, reader, callback, scope, arg){
12246         if(this.fireEvent("beforeload", this, params) !== false){
12247             var  o = {
12248                 params : params || {},
12249                 request: {
12250                     callback : callback,
12251                     scope : scope,
12252                     arg : arg
12253                 },
12254                 reader: reader,
12255                 callback : this.loadResponse,
12256                 scope: this
12257             };
12258             if(this.useAjax){
12259                 Roo.applyIf(o, this.conn);
12260                 if(this.activeRequest){
12261                     Roo.Ajax.abort(this.activeRequest);
12262                 }
12263                 this.activeRequest = Roo.Ajax.request(o);
12264             }else{
12265                 this.conn.request(o);
12266             }
12267         }else{
12268             callback.call(scope||this, null, arg, false);
12269         }
12270     },
12271
12272     // private
12273     loadResponse : function(o, success, response){
12274         delete this.activeRequest;
12275         if(!success){
12276             this.fireEvent("loadexception", this, o, response);
12277             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12278             return;
12279         }
12280         var result;
12281         try {
12282             result = o.reader.read(response);
12283         }catch(e){
12284             this.fireEvent("loadexception", this, o, response, e);
12285             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12286             return;
12287         }
12288         
12289         this.fireEvent("load", this, o, o.request.arg);
12290         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12291     },
12292
12293     // private
12294     update : function(dataSet){
12295
12296     },
12297
12298     // private
12299     updateResponse : function(dataSet){
12300
12301     }
12302 });/*
12303  * Based on:
12304  * Ext JS Library 1.1.1
12305  * Copyright(c) 2006-2007, Ext JS, LLC.
12306  *
12307  * Originally Released Under LGPL - original licence link has changed is not relivant.
12308  *
12309  * Fork - LGPL
12310  * <script type="text/javascript">
12311  */
12312
12313 /**
12314  * @class Roo.data.ScriptTagProxy
12315  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12316  * other than the originating domain of the running page.<br><br>
12317  * <p>
12318  * <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
12319  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12320  * <p>
12321  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12322  * source code that is used as the source inside a &lt;script> tag.<br><br>
12323  * <p>
12324  * In order for the browser to process the returned data, the server must wrap the data object
12325  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12326  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12327  * depending on whether the callback name was passed:
12328  * <p>
12329  * <pre><code>
12330 boolean scriptTag = false;
12331 String cb = request.getParameter("callback");
12332 if (cb != null) {
12333     scriptTag = true;
12334     response.setContentType("text/javascript");
12335 } else {
12336     response.setContentType("application/x-json");
12337 }
12338 Writer out = response.getWriter();
12339 if (scriptTag) {
12340     out.write(cb + "(");
12341 }
12342 out.print(dataBlock.toJsonString());
12343 if (scriptTag) {
12344     out.write(");");
12345 }
12346 </pre></code>
12347  *
12348  * @constructor
12349  * @param {Object} config A configuration object.
12350  */
12351 Roo.data.ScriptTagProxy = function(config){
12352     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12353     Roo.apply(this, config);
12354     this.head = document.getElementsByTagName("head")[0];
12355 };
12356
12357 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12358
12359 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12360     /**
12361      * @cfg {String} url The URL from which to request the data object.
12362      */
12363     /**
12364      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12365      */
12366     timeout : 30000,
12367     /**
12368      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12369      * the server the name of the callback function set up by the load call to process the returned data object.
12370      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12371      * javascript output which calls this named function passing the data object as its only parameter.
12372      */
12373     callbackParam : "callback",
12374     /**
12375      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12376      * name to the request.
12377      */
12378     nocache : true,
12379
12380     /**
12381      * Load data from the configured URL, read the data object into
12382      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12383      * process that block using the passed callback.
12384      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12385      * for the request to the remote server.
12386      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12387      * object into a block of Roo.data.Records.
12388      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12389      * The function must be passed <ul>
12390      * <li>The Record block object</li>
12391      * <li>The "arg" argument from the load function</li>
12392      * <li>A boolean success indicator</li>
12393      * </ul>
12394      * @param {Object} scope The scope in which to call the callback
12395      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12396      */
12397     load : function(params, reader, callback, scope, arg){
12398         if(this.fireEvent("beforeload", this, params) !== false){
12399
12400             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12401
12402             var url = this.url;
12403             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12404             if(this.nocache){
12405                 url += "&_dc=" + (new Date().getTime());
12406             }
12407             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12408             var trans = {
12409                 id : transId,
12410                 cb : "stcCallback"+transId,
12411                 scriptId : "stcScript"+transId,
12412                 params : params,
12413                 arg : arg,
12414                 url : url,
12415                 callback : callback,
12416                 scope : scope,
12417                 reader : reader
12418             };
12419             var conn = this;
12420
12421             window[trans.cb] = function(o){
12422                 conn.handleResponse(o, trans);
12423             };
12424
12425             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12426
12427             if(this.autoAbort !== false){
12428                 this.abort();
12429             }
12430
12431             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12432
12433             var script = document.createElement("script");
12434             script.setAttribute("src", url);
12435             script.setAttribute("type", "text/javascript");
12436             script.setAttribute("id", trans.scriptId);
12437             this.head.appendChild(script);
12438
12439             this.trans = trans;
12440         }else{
12441             callback.call(scope||this, null, arg, false);
12442         }
12443     },
12444
12445     // private
12446     isLoading : function(){
12447         return this.trans ? true : false;
12448     },
12449
12450     /**
12451      * Abort the current server request.
12452      */
12453     abort : function(){
12454         if(this.isLoading()){
12455             this.destroyTrans(this.trans);
12456         }
12457     },
12458
12459     // private
12460     destroyTrans : function(trans, isLoaded){
12461         this.head.removeChild(document.getElementById(trans.scriptId));
12462         clearTimeout(trans.timeoutId);
12463         if(isLoaded){
12464             window[trans.cb] = undefined;
12465             try{
12466                 delete window[trans.cb];
12467             }catch(e){}
12468         }else{
12469             // if hasn't been loaded, wait for load to remove it to prevent script error
12470             window[trans.cb] = function(){
12471                 window[trans.cb] = undefined;
12472                 try{
12473                     delete window[trans.cb];
12474                 }catch(e){}
12475             };
12476         }
12477     },
12478
12479     // private
12480     handleResponse : function(o, trans){
12481         this.trans = false;
12482         this.destroyTrans(trans, true);
12483         var result;
12484         try {
12485             result = trans.reader.readRecords(o);
12486         }catch(e){
12487             this.fireEvent("loadexception", this, o, trans.arg, e);
12488             trans.callback.call(trans.scope||window, null, trans.arg, false);
12489             return;
12490         }
12491         this.fireEvent("load", this, o, trans.arg);
12492         trans.callback.call(trans.scope||window, result, trans.arg, true);
12493     },
12494
12495     // private
12496     handleFailure : function(trans){
12497         this.trans = false;
12498         this.destroyTrans(trans, false);
12499         this.fireEvent("loadexception", this, null, trans.arg);
12500         trans.callback.call(trans.scope||window, null, trans.arg, false);
12501     }
12502 });/*
12503  * Based on:
12504  * Ext JS Library 1.1.1
12505  * Copyright(c) 2006-2007, Ext JS, LLC.
12506  *
12507  * Originally Released Under LGPL - original licence link has changed is not relivant.
12508  *
12509  * Fork - LGPL
12510  * <script type="text/javascript">
12511  */
12512
12513 /**
12514  * @class Roo.data.JsonReader
12515  * @extends Roo.data.DataReader
12516  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12517  * based on mappings in a provided Roo.data.Record constructor.
12518  * 
12519  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12520  * in the reply previously. 
12521  * 
12522  * <p>
12523  * Example code:
12524  * <pre><code>
12525 var RecordDef = Roo.data.Record.create([
12526     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12527     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12528 ]);
12529 var myReader = new Roo.data.JsonReader({
12530     totalProperty: "results",    // The property which contains the total dataset size (optional)
12531     root: "rows",                // The property which contains an Array of row objects
12532     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12533 }, RecordDef);
12534 </code></pre>
12535  * <p>
12536  * This would consume a JSON file like this:
12537  * <pre><code>
12538 { 'results': 2, 'rows': [
12539     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12540     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12541 }
12542 </code></pre>
12543  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12544  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12545  * paged from the remote server.
12546  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12547  * @cfg {String} root name of the property which contains the Array of row objects.
12548  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12549  * @cfg {Array} fields Array of field definition objects
12550  * @constructor
12551  * Create a new JsonReader
12552  * @param {Object} meta Metadata configuration options
12553  * @param {Object} recordType Either an Array of field definition objects,
12554  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12555  */
12556 Roo.data.JsonReader = function(meta, recordType){
12557     
12558     meta = meta || {};
12559     // set some defaults:
12560     Roo.applyIf(meta, {
12561         totalProperty: 'total',
12562         successProperty : 'success',
12563         root : 'data',
12564         id : 'id'
12565     });
12566     
12567     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12568 };
12569 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12570     
12571     /**
12572      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12573      * Used by Store query builder to append _requestMeta to params.
12574      * 
12575      */
12576     metaFromRemote : false,
12577     /**
12578      * This method is only used by a DataProxy which has retrieved data from a remote server.
12579      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12580      * @return {Object} data A data block which is used by an Roo.data.Store object as
12581      * a cache of Roo.data.Records.
12582      */
12583     read : function(response){
12584         var json = response.responseText;
12585        
12586         var o = /* eval:var:o */ eval("("+json+")");
12587         if(!o) {
12588             throw {message: "JsonReader.read: Json object not found"};
12589         }
12590         
12591         if(o.metaData){
12592             
12593             delete this.ef;
12594             this.metaFromRemote = true;
12595             this.meta = o.metaData;
12596             this.recordType = Roo.data.Record.create(o.metaData.fields);
12597             this.onMetaChange(this.meta, this.recordType, o);
12598         }
12599         return this.readRecords(o);
12600     },
12601
12602     // private function a store will implement
12603     onMetaChange : function(meta, recordType, o){
12604
12605     },
12606
12607     /**
12608          * @ignore
12609          */
12610     simpleAccess: function(obj, subsc) {
12611         return obj[subsc];
12612     },
12613
12614         /**
12615          * @ignore
12616          */
12617     getJsonAccessor: function(){
12618         var re = /[\[\.]/;
12619         return function(expr) {
12620             try {
12621                 return(re.test(expr))
12622                     ? new Function("obj", "return obj." + expr)
12623                     : function(obj){
12624                         return obj[expr];
12625                     };
12626             } catch(e){}
12627             return Roo.emptyFn;
12628         };
12629     }(),
12630
12631     /**
12632      * Create a data block containing Roo.data.Records from an XML document.
12633      * @param {Object} o An object which contains an Array of row objects in the property specified
12634      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12635      * which contains the total size of the dataset.
12636      * @return {Object} data A data block which is used by an Roo.data.Store object as
12637      * a cache of Roo.data.Records.
12638      */
12639     readRecords : function(o){
12640         /**
12641          * After any data loads, the raw JSON data is available for further custom processing.
12642          * @type Object
12643          */
12644         this.o = o;
12645         var s = this.meta, Record = this.recordType,
12646             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12647
12648 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12649         if (!this.ef) {
12650             if(s.totalProperty) {
12651                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12652                 }
12653                 if(s.successProperty) {
12654                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12655                 }
12656                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12657                 if (s.id) {
12658                         var g = this.getJsonAccessor(s.id);
12659                         this.getId = function(rec) {
12660                                 var r = g(rec);  
12661                                 return (r === undefined || r === "") ? null : r;
12662                         };
12663                 } else {
12664                         this.getId = function(){return null;};
12665                 }
12666             this.ef = [];
12667             for(var jj = 0; jj < fl; jj++){
12668                 f = fi[jj];
12669                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12670                 this.ef[jj] = this.getJsonAccessor(map);
12671             }
12672         }
12673
12674         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12675         if(s.totalProperty){
12676             var vt = parseInt(this.getTotal(o), 10);
12677             if(!isNaN(vt)){
12678                 totalRecords = vt;
12679             }
12680         }
12681         if(s.successProperty){
12682             var vs = this.getSuccess(o);
12683             if(vs === false || vs === 'false'){
12684                 success = false;
12685             }
12686         }
12687         var records = [];
12688         for(var i = 0; i < c; i++){
12689                 var n = root[i];
12690             var values = {};
12691             var id = this.getId(n);
12692             for(var j = 0; j < fl; j++){
12693                 f = fi[j];
12694             var v = this.ef[j](n);
12695             if (!f.convert) {
12696                 Roo.log('missing convert for ' + f.name);
12697                 Roo.log(f);
12698                 continue;
12699             }
12700             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12701             }
12702             var record = new Record(values, id);
12703             record.json = n;
12704             records[i] = record;
12705         }
12706         return {
12707             raw : o,
12708             success : success,
12709             records : records,
12710             totalRecords : totalRecords
12711         };
12712     }
12713 });/*
12714  * Based on:
12715  * Ext JS Library 1.1.1
12716  * Copyright(c) 2006-2007, Ext JS, LLC.
12717  *
12718  * Originally Released Under LGPL - original licence link has changed is not relivant.
12719  *
12720  * Fork - LGPL
12721  * <script type="text/javascript">
12722  */
12723
12724 /**
12725  * @class Roo.data.ArrayReader
12726  * @extends Roo.data.DataReader
12727  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12728  * Each element of that Array represents a row of data fields. The
12729  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12730  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12731  * <p>
12732  * Example code:.
12733  * <pre><code>
12734 var RecordDef = Roo.data.Record.create([
12735     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12736     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12737 ]);
12738 var myReader = new Roo.data.ArrayReader({
12739     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12740 }, RecordDef);
12741 </code></pre>
12742  * <p>
12743  * This would consume an Array like this:
12744  * <pre><code>
12745 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12746   </code></pre>
12747  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12748  * @constructor
12749  * Create a new JsonReader
12750  * @param {Object} meta Metadata configuration options.
12751  * @param {Object} recordType Either an Array of field definition objects
12752  * as specified to {@link Roo.data.Record#create},
12753  * or an {@link Roo.data.Record} object
12754  * created using {@link Roo.data.Record#create}.
12755  */
12756 Roo.data.ArrayReader = function(meta, recordType){
12757     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12758 };
12759
12760 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12761     /**
12762      * Create a data block containing Roo.data.Records from an XML document.
12763      * @param {Object} o An Array of row objects which represents the dataset.
12764      * @return {Object} data A data block which is used by an Roo.data.Store object as
12765      * a cache of Roo.data.Records.
12766      */
12767     readRecords : function(o){
12768         var sid = this.meta ? this.meta.id : null;
12769         var recordType = this.recordType, fields = recordType.prototype.fields;
12770         var records = [];
12771         var root = o;
12772             for(var i = 0; i < root.length; i++){
12773                     var n = root[i];
12774                 var values = {};
12775                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12776                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12777                 var f = fields.items[j];
12778                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12779                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12780                 v = f.convert(v);
12781                 values[f.name] = v;
12782             }
12783                 var record = new recordType(values, id);
12784                 record.json = n;
12785                 records[records.length] = record;
12786             }
12787             return {
12788                 records : records,
12789                 totalRecords : records.length
12790             };
12791     }
12792 });/*
12793  * - LGPL
12794  * * 
12795  */
12796
12797 /**
12798  * @class Roo.bootstrap.ComboBox
12799  * @extends Roo.bootstrap.TriggerField
12800  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12801  * @cfg {Boolean} append (true|false) default false
12802  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12803  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12804  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12805  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12806  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12807  * @cfg {Boolean} animate default true
12808  * @cfg {Boolean} emptyResultText only for touch device
12809  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12810  * @cfg {String} emptyTitle default ''
12811  * @constructor
12812  * Create a new ComboBox.
12813  * @param {Object} config Configuration options
12814  */
12815 Roo.bootstrap.ComboBox = function(config){
12816     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12817     this.addEvents({
12818         /**
12819          * @event expand
12820          * Fires when the dropdown list is expanded
12821         * @param {Roo.bootstrap.ComboBox} combo This combo box
12822         */
12823         'expand' : true,
12824         /**
12825          * @event collapse
12826          * Fires when the dropdown list is collapsed
12827         * @param {Roo.bootstrap.ComboBox} combo This combo box
12828         */
12829         'collapse' : true,
12830         /**
12831          * @event beforeselect
12832          * Fires before a list item is selected. Return false to cancel the selection.
12833         * @param {Roo.bootstrap.ComboBox} combo This combo box
12834         * @param {Roo.data.Record} record The data record returned from the underlying store
12835         * @param {Number} index The index of the selected item in the dropdown list
12836         */
12837         'beforeselect' : true,
12838         /**
12839          * @event select
12840          * Fires when a list item is selected
12841         * @param {Roo.bootstrap.ComboBox} combo This combo box
12842         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12843         * @param {Number} index The index of the selected item in the dropdown list
12844         */
12845         'select' : true,
12846         /**
12847          * @event beforequery
12848          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12849          * The event object passed has these properties:
12850         * @param {Roo.bootstrap.ComboBox} combo This combo box
12851         * @param {String} query The query
12852         * @param {Boolean} forceAll true to force "all" query
12853         * @param {Boolean} cancel true to cancel the query
12854         * @param {Object} e The query event object
12855         */
12856         'beforequery': true,
12857          /**
12858          * @event add
12859          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12860         * @param {Roo.bootstrap.ComboBox} combo This combo box
12861         */
12862         'add' : true,
12863         /**
12864          * @event edit
12865          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12866         * @param {Roo.bootstrap.ComboBox} combo This combo box
12867         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12868         */
12869         'edit' : true,
12870         /**
12871          * @event remove
12872          * Fires when the remove value from the combobox array
12873         * @param {Roo.bootstrap.ComboBox} combo This combo box
12874         */
12875         'remove' : true,
12876         /**
12877          * @event afterremove
12878          * Fires when the remove value from the combobox array
12879         * @param {Roo.bootstrap.ComboBox} combo This combo box
12880         */
12881         'afterremove' : true,
12882         /**
12883          * @event specialfilter
12884          * Fires when specialfilter
12885             * @param {Roo.bootstrap.ComboBox} combo This combo box
12886             */
12887         'specialfilter' : true,
12888         /**
12889          * @event tick
12890          * Fires when tick the element
12891             * @param {Roo.bootstrap.ComboBox} combo This combo box
12892             */
12893         'tick' : true,
12894         /**
12895          * @event touchviewdisplay
12896          * Fires when touch view require special display (default is using displayField)
12897             * @param {Roo.bootstrap.ComboBox} combo This combo box
12898             * @param {Object} cfg set html .
12899             */
12900         'touchviewdisplay' : true
12901         
12902     });
12903     
12904     this.item = [];
12905     this.tickItems = [];
12906     
12907     this.selectedIndex = -1;
12908     if(this.mode == 'local'){
12909         if(config.queryDelay === undefined){
12910             this.queryDelay = 10;
12911         }
12912         if(config.minChars === undefined){
12913             this.minChars = 0;
12914         }
12915     }
12916 };
12917
12918 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12919      
12920     /**
12921      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12922      * rendering into an Roo.Editor, defaults to false)
12923      */
12924     /**
12925      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12926      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12927      */
12928     /**
12929      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12930      */
12931     /**
12932      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12933      * the dropdown list (defaults to undefined, with no header element)
12934      */
12935
12936      /**
12937      * @cfg {String/Roo.Template} tpl The template to use to render the output
12938      */
12939      
12940      /**
12941      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12942      */
12943     listWidth: undefined,
12944     /**
12945      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12946      * mode = 'remote' or 'text' if mode = 'local')
12947      */
12948     displayField: undefined,
12949     
12950     /**
12951      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12952      * mode = 'remote' or 'value' if mode = 'local'). 
12953      * Note: use of a valueField requires the user make a selection
12954      * in order for a value to be mapped.
12955      */
12956     valueField: undefined,
12957     /**
12958      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12959      */
12960     modalTitle : '',
12961     
12962     /**
12963      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12964      * field's data value (defaults to the underlying DOM element's name)
12965      */
12966     hiddenName: undefined,
12967     /**
12968      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12969      */
12970     listClass: '',
12971     /**
12972      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12973      */
12974     selectedClass: 'active',
12975     
12976     /**
12977      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12978      */
12979     shadow:'sides',
12980     /**
12981      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12982      * anchor positions (defaults to 'tl-bl')
12983      */
12984     listAlign: 'tl-bl?',
12985     /**
12986      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12987      */
12988     maxHeight: 300,
12989     /**
12990      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12991      * query specified by the allQuery config option (defaults to 'query')
12992      */
12993     triggerAction: 'query',
12994     /**
12995      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12996      * (defaults to 4, does not apply if editable = false)
12997      */
12998     minChars : 4,
12999     /**
13000      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13001      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13002      */
13003     typeAhead: false,
13004     /**
13005      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13006      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13007      */
13008     queryDelay: 500,
13009     /**
13010      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13011      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13012      */
13013     pageSize: 0,
13014     /**
13015      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13016      * when editable = true (defaults to false)
13017      */
13018     selectOnFocus:false,
13019     /**
13020      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13021      */
13022     queryParam: 'query',
13023     /**
13024      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13025      * when mode = 'remote' (defaults to 'Loading...')
13026      */
13027     loadingText: 'Loading...',
13028     /**
13029      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13030      */
13031     resizable: false,
13032     /**
13033      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13034      */
13035     handleHeight : 8,
13036     /**
13037      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13038      * traditional select (defaults to true)
13039      */
13040     editable: true,
13041     /**
13042      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13043      */
13044     allQuery: '',
13045     /**
13046      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13047      */
13048     mode: 'remote',
13049     /**
13050      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13051      * listWidth has a higher value)
13052      */
13053     minListWidth : 70,
13054     /**
13055      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13056      * allow the user to set arbitrary text into the field (defaults to false)
13057      */
13058     forceSelection:false,
13059     /**
13060      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13061      * if typeAhead = true (defaults to 250)
13062      */
13063     typeAheadDelay : 250,
13064     /**
13065      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13066      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13067      */
13068     valueNotFoundText : undefined,
13069     /**
13070      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13071      */
13072     blockFocus : false,
13073     
13074     /**
13075      * @cfg {Boolean} disableClear Disable showing of clear button.
13076      */
13077     disableClear : false,
13078     /**
13079      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13080      */
13081     alwaysQuery : false,
13082     
13083     /**
13084      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13085      */
13086     multiple : false,
13087     
13088     /**
13089      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13090      */
13091     invalidClass : "has-warning",
13092     
13093     /**
13094      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13095      */
13096     validClass : "has-success",
13097     
13098     /**
13099      * @cfg {Boolean} specialFilter (true|false) special filter default false
13100      */
13101     specialFilter : false,
13102     
13103     /**
13104      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13105      */
13106     mobileTouchView : true,
13107     
13108     /**
13109      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13110      */
13111     useNativeIOS : false,
13112     
13113     /**
13114      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13115      */
13116     mobile_restrict_height : false,
13117     
13118     ios_options : false,
13119     
13120     //private
13121     addicon : false,
13122     editicon: false,
13123     
13124     page: 0,
13125     hasQuery: false,
13126     append: false,
13127     loadNext: false,
13128     autoFocus : true,
13129     tickable : false,
13130     btnPosition : 'right',
13131     triggerList : true,
13132     showToggleBtn : true,
13133     animate : true,
13134     emptyResultText: 'Empty',
13135     triggerText : 'Select',
13136     emptyTitle : '',
13137     
13138     // element that contains real text value.. (when hidden is used..)
13139     
13140     getAutoCreate : function()
13141     {   
13142         var cfg = false;
13143         //render
13144         /*
13145          * Render classic select for iso
13146          */
13147         
13148         if(Roo.isIOS && this.useNativeIOS){
13149             cfg = this.getAutoCreateNativeIOS();
13150             return cfg;
13151         }
13152         
13153         /*
13154          * Touch Devices
13155          */
13156         
13157         if(Roo.isTouch && this.mobileTouchView){
13158             cfg = this.getAutoCreateTouchView();
13159             return cfg;;
13160         }
13161         
13162         /*
13163          *  Normal ComboBox
13164          */
13165         if(!this.tickable){
13166             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13167             return cfg;
13168         }
13169         
13170         /*
13171          *  ComboBox with tickable selections
13172          */
13173              
13174         var align = this.labelAlign || this.parentLabelAlign();
13175         
13176         cfg = {
13177             cls : 'form-group roo-combobox-tickable' //input-group
13178         };
13179         
13180         var btn_text_select = '';
13181         var btn_text_done = '';
13182         var btn_text_cancel = '';
13183         
13184         if (this.btn_text_show) {
13185             btn_text_select = 'Select';
13186             btn_text_done = 'Done';
13187             btn_text_cancel = 'Cancel'; 
13188         }
13189         
13190         var buttons = {
13191             tag : 'div',
13192             cls : 'tickable-buttons',
13193             cn : [
13194                 {
13195                     tag : 'button',
13196                     type : 'button',
13197                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13198                     //html : this.triggerText
13199                     html: btn_text_select
13200                 },
13201                 {
13202                     tag : 'button',
13203                     type : 'button',
13204                     name : 'ok',
13205                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13206                     //html : 'Done'
13207                     html: btn_text_done
13208                 },
13209                 {
13210                     tag : 'button',
13211                     type : 'button',
13212                     name : 'cancel',
13213                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13214                     //html : 'Cancel'
13215                     html: btn_text_cancel
13216                 }
13217             ]
13218         };
13219         
13220         if(this.editable){
13221             buttons.cn.unshift({
13222                 tag: 'input',
13223                 cls: 'roo-select2-search-field-input'
13224             });
13225         }
13226         
13227         var _this = this;
13228         
13229         Roo.each(buttons.cn, function(c){
13230             if (_this.size) {
13231                 c.cls += ' btn-' + _this.size;
13232             }
13233
13234             if (_this.disabled) {
13235                 c.disabled = true;
13236             }
13237         });
13238         
13239         var box = {
13240             tag: 'div',
13241             cn: [
13242                 {
13243                     tag: 'input',
13244                     type : 'hidden',
13245                     cls: 'form-hidden-field'
13246                 },
13247                 {
13248                     tag: 'ul',
13249                     cls: 'roo-select2-choices',
13250                     cn:[
13251                         {
13252                             tag: 'li',
13253                             cls: 'roo-select2-search-field',
13254                             cn: [
13255                                 buttons
13256                             ]
13257                         }
13258                     ]
13259                 }
13260             ]
13261         };
13262         
13263         var combobox = {
13264             cls: 'roo-select2-container input-group roo-select2-container-multi',
13265             cn: [
13266                 box
13267 //                {
13268 //                    tag: 'ul',
13269 //                    cls: 'typeahead typeahead-long dropdown-menu',
13270 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13271 //                }
13272             ]
13273         };
13274         
13275         if(this.hasFeedback && !this.allowBlank){
13276             
13277             var feedback = {
13278                 tag: 'span',
13279                 cls: 'glyphicon form-control-feedback'
13280             };
13281
13282             combobox.cn.push(feedback);
13283         }
13284         
13285         
13286         if (align ==='left' && this.fieldLabel.length) {
13287             
13288             cfg.cls += ' roo-form-group-label-left';
13289             
13290             cfg.cn = [
13291                 {
13292                     tag : 'i',
13293                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13294                     tooltip : 'This field is required'
13295                 },
13296                 {
13297                     tag: 'label',
13298                     'for' :  id,
13299                     cls : 'control-label',
13300                     html : this.fieldLabel
13301
13302                 },
13303                 {
13304                     cls : "", 
13305                     cn: [
13306                         combobox
13307                     ]
13308                 }
13309
13310             ];
13311             
13312             var labelCfg = cfg.cn[1];
13313             var contentCfg = cfg.cn[2];
13314             
13315
13316             if(this.indicatorpos == 'right'){
13317                 
13318                 cfg.cn = [
13319                     {
13320                         tag: 'label',
13321                         'for' :  id,
13322                         cls : 'control-label',
13323                         cn : [
13324                             {
13325                                 tag : 'span',
13326                                 html : this.fieldLabel
13327                             },
13328                             {
13329                                 tag : 'i',
13330                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13331                                 tooltip : 'This field is required'
13332                             }
13333                         ]
13334                     },
13335                     {
13336                         cls : "",
13337                         cn: [
13338                             combobox
13339                         ]
13340                     }
13341
13342                 ];
13343                 
13344                 
13345                 
13346                 labelCfg = cfg.cn[0];
13347                 contentCfg = cfg.cn[1];
13348             
13349             }
13350             
13351             if(this.labelWidth > 12){
13352                 labelCfg.style = "width: " + this.labelWidth + 'px';
13353             }
13354             
13355             if(this.labelWidth < 13 && this.labelmd == 0){
13356                 this.labelmd = this.labelWidth;
13357             }
13358             
13359             if(this.labellg > 0){
13360                 labelCfg.cls += ' col-lg-' + this.labellg;
13361                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13362             }
13363             
13364             if(this.labelmd > 0){
13365                 labelCfg.cls += ' col-md-' + this.labelmd;
13366                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13367             }
13368             
13369             if(this.labelsm > 0){
13370                 labelCfg.cls += ' col-sm-' + this.labelsm;
13371                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13372             }
13373             
13374             if(this.labelxs > 0){
13375                 labelCfg.cls += ' col-xs-' + this.labelxs;
13376                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13377             }
13378                 
13379                 
13380         } else if ( this.fieldLabel.length) {
13381 //                Roo.log(" label");
13382                  cfg.cn = [
13383                     {
13384                         tag : 'i',
13385                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13386                         tooltip : 'This field is required'
13387                     },
13388                     {
13389                         tag: 'label',
13390                         //cls : 'input-group-addon',
13391                         html : this.fieldLabel
13392                     },
13393                     combobox
13394                 ];
13395                 
13396                 if(this.indicatorpos == 'right'){
13397                     cfg.cn = [
13398                         {
13399                             tag: 'label',
13400                             //cls : 'input-group-addon',
13401                             html : this.fieldLabel
13402                         },
13403                         {
13404                             tag : 'i',
13405                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13406                             tooltip : 'This field is required'
13407                         },
13408                         combobox
13409                     ];
13410                     
13411                 }
13412
13413         } else {
13414             
13415 //                Roo.log(" no label && no align");
13416                 cfg = combobox
13417                      
13418                 
13419         }
13420          
13421         var settings=this;
13422         ['xs','sm','md','lg'].map(function(size){
13423             if (settings[size]) {
13424                 cfg.cls += ' col-' + size + '-' + settings[size];
13425             }
13426         });
13427         
13428         return cfg;
13429         
13430     },
13431     
13432     _initEventsCalled : false,
13433     
13434     // private
13435     initEvents: function()
13436     {   
13437         if (this._initEventsCalled) { // as we call render... prevent looping...
13438             return;
13439         }
13440         this._initEventsCalled = true;
13441         
13442         if (!this.store) {
13443             throw "can not find store for combo";
13444         }
13445         
13446         this.indicator = this.indicatorEl();
13447         
13448         this.store = Roo.factory(this.store, Roo.data);
13449         this.store.parent = this;
13450         
13451         // if we are building from html. then this element is so complex, that we can not really
13452         // use the rendered HTML.
13453         // so we have to trash and replace the previous code.
13454         if (Roo.XComponent.build_from_html) {
13455             // remove this element....
13456             var e = this.el.dom, k=0;
13457             while (e ) { e = e.previousSibling;  ++k;}
13458
13459             this.el.remove();
13460             
13461             this.el=false;
13462             this.rendered = false;
13463             
13464             this.render(this.parent().getChildContainer(true), k);
13465         }
13466         
13467         if(Roo.isIOS && this.useNativeIOS){
13468             this.initIOSView();
13469             return;
13470         }
13471         
13472         /*
13473          * Touch Devices
13474          */
13475         
13476         if(Roo.isTouch && this.mobileTouchView){
13477             this.initTouchView();
13478             return;
13479         }
13480         
13481         if(this.tickable){
13482             this.initTickableEvents();
13483             return;
13484         }
13485         
13486         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13487         
13488         if(this.hiddenName){
13489             
13490             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13491             
13492             this.hiddenField.dom.value =
13493                 this.hiddenValue !== undefined ? this.hiddenValue :
13494                 this.value !== undefined ? this.value : '';
13495
13496             // prevent input submission
13497             this.el.dom.removeAttribute('name');
13498             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13499              
13500              
13501         }
13502         //if(Roo.isGecko){
13503         //    this.el.dom.setAttribute('autocomplete', 'off');
13504         //}
13505         
13506         var cls = 'x-combo-list';
13507         
13508         //this.list = new Roo.Layer({
13509         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13510         //});
13511         
13512         var _this = this;
13513         
13514         (function(){
13515             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13516             _this.list.setWidth(lw);
13517         }).defer(100);
13518         
13519         this.list.on('mouseover', this.onViewOver, this);
13520         this.list.on('mousemove', this.onViewMove, this);
13521         this.list.on('scroll', this.onViewScroll, this);
13522         
13523         /*
13524         this.list.swallowEvent('mousewheel');
13525         this.assetHeight = 0;
13526
13527         if(this.title){
13528             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13529             this.assetHeight += this.header.getHeight();
13530         }
13531
13532         this.innerList = this.list.createChild({cls:cls+'-inner'});
13533         this.innerList.on('mouseover', this.onViewOver, this);
13534         this.innerList.on('mousemove', this.onViewMove, this);
13535         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13536         
13537         if(this.allowBlank && !this.pageSize && !this.disableClear){
13538             this.footer = this.list.createChild({cls:cls+'-ft'});
13539             this.pageTb = new Roo.Toolbar(this.footer);
13540            
13541         }
13542         if(this.pageSize){
13543             this.footer = this.list.createChild({cls:cls+'-ft'});
13544             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13545                     {pageSize: this.pageSize});
13546             
13547         }
13548         
13549         if (this.pageTb && this.allowBlank && !this.disableClear) {
13550             var _this = this;
13551             this.pageTb.add(new Roo.Toolbar.Fill(), {
13552                 cls: 'x-btn-icon x-btn-clear',
13553                 text: '&#160;',
13554                 handler: function()
13555                 {
13556                     _this.collapse();
13557                     _this.clearValue();
13558                     _this.onSelect(false, -1);
13559                 }
13560             });
13561         }
13562         if (this.footer) {
13563             this.assetHeight += this.footer.getHeight();
13564         }
13565         */
13566             
13567         if(!this.tpl){
13568             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13569         }
13570
13571         this.view = new Roo.View(this.list, this.tpl, {
13572             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13573         });
13574         //this.view.wrapEl.setDisplayed(false);
13575         this.view.on('click', this.onViewClick, this);
13576         
13577         
13578         this.store.on('beforeload', this.onBeforeLoad, this);
13579         this.store.on('load', this.onLoad, this);
13580         this.store.on('loadexception', this.onLoadException, this);
13581         /*
13582         if(this.resizable){
13583             this.resizer = new Roo.Resizable(this.list,  {
13584                pinned:true, handles:'se'
13585             });
13586             this.resizer.on('resize', function(r, w, h){
13587                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13588                 this.listWidth = w;
13589                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13590                 this.restrictHeight();
13591             }, this);
13592             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13593         }
13594         */
13595         if(!this.editable){
13596             this.editable = true;
13597             this.setEditable(false);
13598         }
13599         
13600         /*
13601         
13602         if (typeof(this.events.add.listeners) != 'undefined') {
13603             
13604             this.addicon = this.wrap.createChild(
13605                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13606        
13607             this.addicon.on('click', function(e) {
13608                 this.fireEvent('add', this);
13609             }, this);
13610         }
13611         if (typeof(this.events.edit.listeners) != 'undefined') {
13612             
13613             this.editicon = this.wrap.createChild(
13614                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13615             if (this.addicon) {
13616                 this.editicon.setStyle('margin-left', '40px');
13617             }
13618             this.editicon.on('click', function(e) {
13619                 
13620                 // we fire even  if inothing is selected..
13621                 this.fireEvent('edit', this, this.lastData );
13622                 
13623             }, this);
13624         }
13625         */
13626         
13627         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13628             "up" : function(e){
13629                 this.inKeyMode = true;
13630                 this.selectPrev();
13631             },
13632
13633             "down" : function(e){
13634                 if(!this.isExpanded()){
13635                     this.onTriggerClick();
13636                 }else{
13637                     this.inKeyMode = true;
13638                     this.selectNext();
13639                 }
13640             },
13641
13642             "enter" : function(e){
13643 //                this.onViewClick();
13644                 //return true;
13645                 this.collapse();
13646                 
13647                 if(this.fireEvent("specialkey", this, e)){
13648                     this.onViewClick(false);
13649                 }
13650                 
13651                 return true;
13652             },
13653
13654             "esc" : function(e){
13655                 this.collapse();
13656             },
13657
13658             "tab" : function(e){
13659                 this.collapse();
13660                 
13661                 if(this.fireEvent("specialkey", this, e)){
13662                     this.onViewClick(false);
13663                 }
13664                 
13665                 return true;
13666             },
13667
13668             scope : this,
13669
13670             doRelay : function(foo, bar, hname){
13671                 if(hname == 'down' || this.scope.isExpanded()){
13672                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13673                 }
13674                 return true;
13675             },
13676
13677             forceKeyDown: true
13678         });
13679         
13680         
13681         this.queryDelay = Math.max(this.queryDelay || 10,
13682                 this.mode == 'local' ? 10 : 250);
13683         
13684         
13685         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13686         
13687         if(this.typeAhead){
13688             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13689         }
13690         if(this.editable !== false){
13691             this.inputEl().on("keyup", this.onKeyUp, this);
13692         }
13693         if(this.forceSelection){
13694             this.inputEl().on('blur', this.doForce, this);
13695         }
13696         
13697         if(this.multiple){
13698             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13699             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13700         }
13701     },
13702     
13703     initTickableEvents: function()
13704     {   
13705         this.createList();
13706         
13707         if(this.hiddenName){
13708             
13709             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13710             
13711             this.hiddenField.dom.value =
13712                 this.hiddenValue !== undefined ? this.hiddenValue :
13713                 this.value !== undefined ? this.value : '';
13714
13715             // prevent input submission
13716             this.el.dom.removeAttribute('name');
13717             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13718              
13719              
13720         }
13721         
13722 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13723         
13724         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13725         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13726         if(this.triggerList){
13727             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13728         }
13729          
13730         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13731         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13732         
13733         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13734         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13735         
13736         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13737         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13738         
13739         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13740         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13741         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13742         
13743         this.okBtn.hide();
13744         this.cancelBtn.hide();
13745         
13746         var _this = this;
13747         
13748         (function(){
13749             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13750             _this.list.setWidth(lw);
13751         }).defer(100);
13752         
13753         this.list.on('mouseover', this.onViewOver, this);
13754         this.list.on('mousemove', this.onViewMove, this);
13755         
13756         this.list.on('scroll', this.onViewScroll, this);
13757         
13758         if(!this.tpl){
13759             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13760                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13761         }
13762
13763         this.view = new Roo.View(this.list, this.tpl, {
13764             singleSelect:true,
13765             tickable:true,
13766             parent:this,
13767             store: this.store,
13768             selectedClass: this.selectedClass
13769         });
13770         
13771         //this.view.wrapEl.setDisplayed(false);
13772         this.view.on('click', this.onViewClick, this);
13773         
13774         
13775         
13776         this.store.on('beforeload', this.onBeforeLoad, this);
13777         this.store.on('load', this.onLoad, this);
13778         this.store.on('loadexception', this.onLoadException, this);
13779         
13780         if(this.editable){
13781             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13782                 "up" : function(e){
13783                     this.inKeyMode = true;
13784                     this.selectPrev();
13785                 },
13786
13787                 "down" : function(e){
13788                     this.inKeyMode = true;
13789                     this.selectNext();
13790                 },
13791
13792                 "enter" : function(e){
13793                     if(this.fireEvent("specialkey", this, e)){
13794                         this.onViewClick(false);
13795                     }
13796                     
13797                     return true;
13798                 },
13799
13800                 "esc" : function(e){
13801                     this.onTickableFooterButtonClick(e, false, false);
13802                 },
13803
13804                 "tab" : function(e){
13805                     this.fireEvent("specialkey", this, e);
13806                     
13807                     this.onTickableFooterButtonClick(e, false, false);
13808                     
13809                     return true;
13810                 },
13811
13812                 scope : this,
13813
13814                 doRelay : function(e, fn, key){
13815                     if(this.scope.isExpanded()){
13816                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13817                     }
13818                     return true;
13819                 },
13820
13821                 forceKeyDown: true
13822             });
13823         }
13824         
13825         this.queryDelay = Math.max(this.queryDelay || 10,
13826                 this.mode == 'local' ? 10 : 250);
13827         
13828         
13829         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13830         
13831         if(this.typeAhead){
13832             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13833         }
13834         
13835         if(this.editable !== false){
13836             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13837         }
13838         
13839         this.indicator = this.indicatorEl();
13840         
13841         if(this.indicator){
13842             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13843             this.indicator.hide();
13844         }
13845         
13846     },
13847
13848     onDestroy : function(){
13849         if(this.view){
13850             this.view.setStore(null);
13851             this.view.el.removeAllListeners();
13852             this.view.el.remove();
13853             this.view.purgeListeners();
13854         }
13855         if(this.list){
13856             this.list.dom.innerHTML  = '';
13857         }
13858         
13859         if(this.store){
13860             this.store.un('beforeload', this.onBeforeLoad, this);
13861             this.store.un('load', this.onLoad, this);
13862             this.store.un('loadexception', this.onLoadException, this);
13863         }
13864         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13865     },
13866
13867     // private
13868     fireKey : function(e){
13869         if(e.isNavKeyPress() && !this.list.isVisible()){
13870             this.fireEvent("specialkey", this, e);
13871         }
13872     },
13873
13874     // private
13875     onResize: function(w, h){
13876 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13877 //        
13878 //        if(typeof w != 'number'){
13879 //            // we do not handle it!?!?
13880 //            return;
13881 //        }
13882 //        var tw = this.trigger.getWidth();
13883 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13884 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13885 //        var x = w - tw;
13886 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13887 //            
13888 //        //this.trigger.setStyle('left', x+'px');
13889 //        
13890 //        if(this.list && this.listWidth === undefined){
13891 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13892 //            this.list.setWidth(lw);
13893 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13894 //        }
13895         
13896     
13897         
13898     },
13899
13900     /**
13901      * Allow or prevent the user from directly editing the field text.  If false is passed,
13902      * the user will only be able to select from the items defined in the dropdown list.  This method
13903      * is the runtime equivalent of setting the 'editable' config option at config time.
13904      * @param {Boolean} value True to allow the user to directly edit the field text
13905      */
13906     setEditable : function(value){
13907         if(value == this.editable){
13908             return;
13909         }
13910         this.editable = value;
13911         if(!value){
13912             this.inputEl().dom.setAttribute('readOnly', true);
13913             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13914             this.inputEl().addClass('x-combo-noedit');
13915         }else{
13916             this.inputEl().dom.setAttribute('readOnly', false);
13917             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13918             this.inputEl().removeClass('x-combo-noedit');
13919         }
13920     },
13921
13922     // private
13923     
13924     onBeforeLoad : function(combo,opts){
13925         if(!this.hasFocus){
13926             return;
13927         }
13928          if (!opts.add) {
13929             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13930          }
13931         this.restrictHeight();
13932         this.selectedIndex = -1;
13933     },
13934
13935     // private
13936     onLoad : function(){
13937         
13938         this.hasQuery = false;
13939         
13940         if(!this.hasFocus){
13941             return;
13942         }
13943         
13944         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13945             this.loading.hide();
13946         }
13947         
13948         if(this.store.getCount() > 0){
13949             
13950             this.expand();
13951             this.restrictHeight();
13952             if(this.lastQuery == this.allQuery){
13953                 if(this.editable && !this.tickable){
13954                     this.inputEl().dom.select();
13955                 }
13956                 
13957                 if(
13958                     !this.selectByValue(this.value, true) &&
13959                     this.autoFocus && 
13960                     (
13961                         !this.store.lastOptions ||
13962                         typeof(this.store.lastOptions.add) == 'undefined' || 
13963                         this.store.lastOptions.add != true
13964                     )
13965                 ){
13966                     this.select(0, true);
13967                 }
13968             }else{
13969                 if(this.autoFocus){
13970                     this.selectNext();
13971                 }
13972                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13973                     this.taTask.delay(this.typeAheadDelay);
13974                 }
13975             }
13976         }else{
13977             this.onEmptyResults();
13978         }
13979         
13980         //this.el.focus();
13981     },
13982     // private
13983     onLoadException : function()
13984     {
13985         this.hasQuery = false;
13986         
13987         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13988             this.loading.hide();
13989         }
13990         
13991         if(this.tickable && this.editable){
13992             return;
13993         }
13994         
13995         this.collapse();
13996         // only causes errors at present
13997         //Roo.log(this.store.reader.jsonData);
13998         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13999             // fixme
14000             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14001         //}
14002         
14003         
14004     },
14005     // private
14006     onTypeAhead : function(){
14007         if(this.store.getCount() > 0){
14008             var r = this.store.getAt(0);
14009             var newValue = r.data[this.displayField];
14010             var len = newValue.length;
14011             var selStart = this.getRawValue().length;
14012             
14013             if(selStart != len){
14014                 this.setRawValue(newValue);
14015                 this.selectText(selStart, newValue.length);
14016             }
14017         }
14018     },
14019
14020     // private
14021     onSelect : function(record, index){
14022         
14023         if(this.fireEvent('beforeselect', this, record, index) !== false){
14024         
14025             this.setFromData(index > -1 ? record.data : false);
14026             
14027             this.collapse();
14028             this.fireEvent('select', this, record, index);
14029         }
14030     },
14031
14032     /**
14033      * Returns the currently selected field value or empty string if no value is set.
14034      * @return {String} value The selected value
14035      */
14036     getValue : function()
14037     {
14038         if(Roo.isIOS && this.useNativeIOS){
14039             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14040         }
14041         
14042         if(this.multiple){
14043             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14044         }
14045         
14046         if(this.valueField){
14047             return typeof this.value != 'undefined' ? this.value : '';
14048         }else{
14049             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14050         }
14051     },
14052     
14053     getRawValue : function()
14054     {
14055         if(Roo.isIOS && this.useNativeIOS){
14056             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14057         }
14058         
14059         var v = this.inputEl().getValue();
14060         
14061         return v;
14062     },
14063
14064     /**
14065      * Clears any text/value currently set in the field
14066      */
14067     clearValue : function(){
14068         
14069         if(this.hiddenField){
14070             this.hiddenField.dom.value = '';
14071         }
14072         this.value = '';
14073         this.setRawValue('');
14074         this.lastSelectionText = '';
14075         this.lastData = false;
14076         
14077         var close = this.closeTriggerEl();
14078         
14079         if(close){
14080             close.hide();
14081         }
14082         
14083         this.validate();
14084         
14085     },
14086
14087     /**
14088      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14089      * will be displayed in the field.  If the value does not match the data value of an existing item,
14090      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14091      * Otherwise the field will be blank (although the value will still be set).
14092      * @param {String} value The value to match
14093      */
14094     setValue : function(v)
14095     {
14096         if(Roo.isIOS && this.useNativeIOS){
14097             this.setIOSValue(v);
14098             return;
14099         }
14100         
14101         if(this.multiple){
14102             this.syncValue();
14103             return;
14104         }
14105         
14106         var text = v;
14107         if(this.valueField){
14108             var r = this.findRecord(this.valueField, v);
14109             if(r){
14110                 text = r.data[this.displayField];
14111             }else if(this.valueNotFoundText !== undefined){
14112                 text = this.valueNotFoundText;
14113             }
14114         }
14115         this.lastSelectionText = text;
14116         if(this.hiddenField){
14117             this.hiddenField.dom.value = v;
14118         }
14119         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14120         this.value = v;
14121         
14122         var close = this.closeTriggerEl();
14123         
14124         if(close){
14125             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14126         }
14127         
14128         this.validate();
14129     },
14130     /**
14131      * @property {Object} the last set data for the element
14132      */
14133     
14134     lastData : false,
14135     /**
14136      * Sets the value of the field based on a object which is related to the record format for the store.
14137      * @param {Object} value the value to set as. or false on reset?
14138      */
14139     setFromData : function(o){
14140         
14141         if(this.multiple){
14142             this.addItem(o);
14143             return;
14144         }
14145             
14146         var dv = ''; // display value
14147         var vv = ''; // value value..
14148         this.lastData = o;
14149         if (this.displayField) {
14150             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14151         } else {
14152             // this is an error condition!!!
14153             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14154         }
14155         
14156         if(this.valueField){
14157             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14158         }
14159         
14160         var close = this.closeTriggerEl();
14161         
14162         if(close){
14163             if(dv.length || vv * 1 > 0){
14164                 close.show() ;
14165                 this.blockFocus=true;
14166             } else {
14167                 close.hide();
14168             }             
14169         }
14170         
14171         if(this.hiddenField){
14172             this.hiddenField.dom.value = vv;
14173             
14174             this.lastSelectionText = dv;
14175             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14176             this.value = vv;
14177             return;
14178         }
14179         // no hidden field.. - we store the value in 'value', but still display
14180         // display field!!!!
14181         this.lastSelectionText = dv;
14182         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14183         this.value = vv;
14184         
14185         
14186         
14187     },
14188     // private
14189     reset : function(){
14190         // overridden so that last data is reset..
14191         
14192         if(this.multiple){
14193             this.clearItem();
14194             return;
14195         }
14196         
14197         this.setValue(this.originalValue);
14198         //this.clearInvalid();
14199         this.lastData = false;
14200         if (this.view) {
14201             this.view.clearSelections();
14202         }
14203         
14204         this.validate();
14205     },
14206     // private
14207     findRecord : function(prop, value){
14208         var record;
14209         if(this.store.getCount() > 0){
14210             this.store.each(function(r){
14211                 if(r.data[prop] == value){
14212                     record = r;
14213                     return false;
14214                 }
14215                 return true;
14216             });
14217         }
14218         return record;
14219     },
14220     
14221     getName: function()
14222     {
14223         // returns hidden if it's set..
14224         if (!this.rendered) {return ''};
14225         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14226         
14227     },
14228     // private
14229     onViewMove : function(e, t){
14230         this.inKeyMode = false;
14231     },
14232
14233     // private
14234     onViewOver : function(e, t){
14235         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14236             return;
14237         }
14238         var item = this.view.findItemFromChild(t);
14239         
14240         if(item){
14241             var index = this.view.indexOf(item);
14242             this.select(index, false);
14243         }
14244     },
14245
14246     // private
14247     onViewClick : function(view, doFocus, el, e)
14248     {
14249         var index = this.view.getSelectedIndexes()[0];
14250         
14251         var r = this.store.getAt(index);
14252         
14253         if(this.tickable){
14254             
14255             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14256                 return;
14257             }
14258             
14259             var rm = false;
14260             var _this = this;
14261             
14262             Roo.each(this.tickItems, function(v,k){
14263                 
14264                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14265                     Roo.log(v);
14266                     _this.tickItems.splice(k, 1);
14267                     
14268                     if(typeof(e) == 'undefined' && view == false){
14269                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14270                     }
14271                     
14272                     rm = true;
14273                     return;
14274                 }
14275             });
14276             
14277             if(rm){
14278                 return;
14279             }
14280             
14281             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14282                 this.tickItems.push(r.data);
14283             }
14284             
14285             if(typeof(e) == 'undefined' && view == false){
14286                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14287             }
14288                     
14289             return;
14290         }
14291         
14292         if(r){
14293             this.onSelect(r, index);
14294         }
14295         if(doFocus !== false && !this.blockFocus){
14296             this.inputEl().focus();
14297         }
14298     },
14299
14300     // private
14301     restrictHeight : function(){
14302         //this.innerList.dom.style.height = '';
14303         //var inner = this.innerList.dom;
14304         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14305         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14306         //this.list.beginUpdate();
14307         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14308         this.list.alignTo(this.inputEl(), this.listAlign);
14309         this.list.alignTo(this.inputEl(), this.listAlign);
14310         //this.list.endUpdate();
14311     },
14312
14313     // private
14314     onEmptyResults : function(){
14315         
14316         if(this.tickable && this.editable){
14317             this.hasFocus = false;
14318             this.restrictHeight();
14319             return;
14320         }
14321         
14322         this.collapse();
14323     },
14324
14325     /**
14326      * Returns true if the dropdown list is expanded, else false.
14327      */
14328     isExpanded : function(){
14329         return this.list.isVisible();
14330     },
14331
14332     /**
14333      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14334      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14335      * @param {String} value The data value of the item to select
14336      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14337      * selected item if it is not currently in view (defaults to true)
14338      * @return {Boolean} True if the value matched an item in the list, else false
14339      */
14340     selectByValue : function(v, scrollIntoView){
14341         if(v !== undefined && v !== null){
14342             var r = this.findRecord(this.valueField || this.displayField, v);
14343             if(r){
14344                 this.select(this.store.indexOf(r), scrollIntoView);
14345                 return true;
14346             }
14347         }
14348         return false;
14349     },
14350
14351     /**
14352      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14353      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14354      * @param {Number} index The zero-based index of the list item to select
14355      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14356      * selected item if it is not currently in view (defaults to true)
14357      */
14358     select : function(index, scrollIntoView){
14359         this.selectedIndex = index;
14360         this.view.select(index);
14361         if(scrollIntoView !== false){
14362             var el = this.view.getNode(index);
14363             /*
14364              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14365              */
14366             if(el){
14367                 this.list.scrollChildIntoView(el, false);
14368             }
14369         }
14370     },
14371
14372     // private
14373     selectNext : function(){
14374         var ct = this.store.getCount();
14375         if(ct > 0){
14376             if(this.selectedIndex == -1){
14377                 this.select(0);
14378             }else if(this.selectedIndex < ct-1){
14379                 this.select(this.selectedIndex+1);
14380             }
14381         }
14382     },
14383
14384     // private
14385     selectPrev : function(){
14386         var ct = this.store.getCount();
14387         if(ct > 0){
14388             if(this.selectedIndex == -1){
14389                 this.select(0);
14390             }else if(this.selectedIndex != 0){
14391                 this.select(this.selectedIndex-1);
14392             }
14393         }
14394     },
14395
14396     // private
14397     onKeyUp : function(e){
14398         if(this.editable !== false && !e.isSpecialKey()){
14399             this.lastKey = e.getKey();
14400             this.dqTask.delay(this.queryDelay);
14401         }
14402     },
14403
14404     // private
14405     validateBlur : function(){
14406         return !this.list || !this.list.isVisible();   
14407     },
14408
14409     // private
14410     initQuery : function(){
14411         
14412         var v = this.getRawValue();
14413         
14414         if(this.tickable && this.editable){
14415             v = this.tickableInputEl().getValue();
14416         }
14417         
14418         this.doQuery(v);
14419     },
14420
14421     // private
14422     doForce : function(){
14423         if(this.inputEl().dom.value.length > 0){
14424             this.inputEl().dom.value =
14425                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14426              
14427         }
14428     },
14429
14430     /**
14431      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14432      * query allowing the query action to be canceled if needed.
14433      * @param {String} query The SQL query to execute
14434      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14435      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14436      * saved in the current store (defaults to false)
14437      */
14438     doQuery : function(q, forceAll){
14439         
14440         if(q === undefined || q === null){
14441             q = '';
14442         }
14443         var qe = {
14444             query: q,
14445             forceAll: forceAll,
14446             combo: this,
14447             cancel:false
14448         };
14449         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14450             return false;
14451         }
14452         q = qe.query;
14453         
14454         forceAll = qe.forceAll;
14455         if(forceAll === true || (q.length >= this.minChars)){
14456             
14457             this.hasQuery = true;
14458             
14459             if(this.lastQuery != q || this.alwaysQuery){
14460                 this.lastQuery = q;
14461                 if(this.mode == 'local'){
14462                     this.selectedIndex = -1;
14463                     if(forceAll){
14464                         this.store.clearFilter();
14465                     }else{
14466                         
14467                         if(this.specialFilter){
14468                             this.fireEvent('specialfilter', this);
14469                             this.onLoad();
14470                             return;
14471                         }
14472                         
14473                         this.store.filter(this.displayField, q);
14474                     }
14475                     
14476                     this.store.fireEvent("datachanged", this.store);
14477                     
14478                     this.onLoad();
14479                     
14480                     
14481                 }else{
14482                     
14483                     this.store.baseParams[this.queryParam] = q;
14484                     
14485                     var options = {params : this.getParams(q)};
14486                     
14487                     if(this.loadNext){
14488                         options.add = true;
14489                         options.params.start = this.page * this.pageSize;
14490                     }
14491                     
14492                     this.store.load(options);
14493                     
14494                     /*
14495                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14496                      *  we should expand the list on onLoad
14497                      *  so command out it
14498                      */
14499 //                    this.expand();
14500                 }
14501             }else{
14502                 this.selectedIndex = -1;
14503                 this.onLoad();   
14504             }
14505         }
14506         
14507         this.loadNext = false;
14508     },
14509     
14510     // private
14511     getParams : function(q){
14512         var p = {};
14513         //p[this.queryParam] = q;
14514         
14515         if(this.pageSize){
14516             p.start = 0;
14517             p.limit = this.pageSize;
14518         }
14519         return p;
14520     },
14521
14522     /**
14523      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14524      */
14525     collapse : function(){
14526         if(!this.isExpanded()){
14527             return;
14528         }
14529         
14530         this.list.hide();
14531         
14532         this.hasFocus = false;
14533         
14534         if(this.tickable){
14535             this.okBtn.hide();
14536             this.cancelBtn.hide();
14537             this.trigger.show();
14538             
14539             if(this.editable){
14540                 this.tickableInputEl().dom.value = '';
14541                 this.tickableInputEl().blur();
14542             }
14543             
14544         }
14545         
14546         Roo.get(document).un('mousedown', this.collapseIf, this);
14547         Roo.get(document).un('mousewheel', this.collapseIf, this);
14548         if (!this.editable) {
14549             Roo.get(document).un('keydown', this.listKeyPress, this);
14550         }
14551         this.fireEvent('collapse', this);
14552         
14553         this.validate();
14554     },
14555
14556     // private
14557     collapseIf : function(e){
14558         var in_combo  = e.within(this.el);
14559         var in_list =  e.within(this.list);
14560         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14561         
14562         if (in_combo || in_list || is_list) {
14563             //e.stopPropagation();
14564             return;
14565         }
14566         
14567         if(this.tickable){
14568             this.onTickableFooterButtonClick(e, false, false);
14569         }
14570
14571         this.collapse();
14572         
14573     },
14574
14575     /**
14576      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14577      */
14578     expand : function(){
14579        
14580         if(this.isExpanded() || !this.hasFocus){
14581             return;
14582         }
14583         
14584         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14585         this.list.setWidth(lw);
14586         
14587         Roo.log('expand');
14588         
14589         this.list.show();
14590         
14591         this.restrictHeight();
14592         
14593         if(this.tickable){
14594             
14595             this.tickItems = Roo.apply([], this.item);
14596             
14597             this.okBtn.show();
14598             this.cancelBtn.show();
14599             this.trigger.hide();
14600             
14601             if(this.editable){
14602                 this.tickableInputEl().focus();
14603             }
14604             
14605         }
14606         
14607         Roo.get(document).on('mousedown', this.collapseIf, this);
14608         Roo.get(document).on('mousewheel', this.collapseIf, this);
14609         if (!this.editable) {
14610             Roo.get(document).on('keydown', this.listKeyPress, this);
14611         }
14612         
14613         this.fireEvent('expand', this);
14614     },
14615
14616     // private
14617     // Implements the default empty TriggerField.onTriggerClick function
14618     onTriggerClick : function(e)
14619     {
14620         Roo.log('trigger click');
14621         
14622         if(this.disabled || !this.triggerList){
14623             return;
14624         }
14625         
14626         this.page = 0;
14627         this.loadNext = false;
14628         
14629         if(this.isExpanded()){
14630             this.collapse();
14631             if (!this.blockFocus) {
14632                 this.inputEl().focus();
14633             }
14634             
14635         }else {
14636             this.hasFocus = true;
14637             if(this.triggerAction == 'all') {
14638                 this.doQuery(this.allQuery, true);
14639             } else {
14640                 this.doQuery(this.getRawValue());
14641             }
14642             if (!this.blockFocus) {
14643                 this.inputEl().focus();
14644             }
14645         }
14646     },
14647     
14648     onTickableTriggerClick : function(e)
14649     {
14650         if(this.disabled){
14651             return;
14652         }
14653         
14654         this.page = 0;
14655         this.loadNext = false;
14656         this.hasFocus = true;
14657         
14658         if(this.triggerAction == 'all') {
14659             this.doQuery(this.allQuery, true);
14660         } else {
14661             this.doQuery(this.getRawValue());
14662         }
14663     },
14664     
14665     onSearchFieldClick : function(e)
14666     {
14667         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14668             this.onTickableFooterButtonClick(e, false, false);
14669             return;
14670         }
14671         
14672         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14673             return;
14674         }
14675         
14676         this.page = 0;
14677         this.loadNext = false;
14678         this.hasFocus = true;
14679         
14680         if(this.triggerAction == 'all') {
14681             this.doQuery(this.allQuery, true);
14682         } else {
14683             this.doQuery(this.getRawValue());
14684         }
14685     },
14686     
14687     listKeyPress : function(e)
14688     {
14689         //Roo.log('listkeypress');
14690         // scroll to first matching element based on key pres..
14691         if (e.isSpecialKey()) {
14692             return false;
14693         }
14694         var k = String.fromCharCode(e.getKey()).toUpperCase();
14695         //Roo.log(k);
14696         var match  = false;
14697         var csel = this.view.getSelectedNodes();
14698         var cselitem = false;
14699         if (csel.length) {
14700             var ix = this.view.indexOf(csel[0]);
14701             cselitem  = this.store.getAt(ix);
14702             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14703                 cselitem = false;
14704             }
14705             
14706         }
14707         
14708         this.store.each(function(v) { 
14709             if (cselitem) {
14710                 // start at existing selection.
14711                 if (cselitem.id == v.id) {
14712                     cselitem = false;
14713                 }
14714                 return true;
14715             }
14716                 
14717             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14718                 match = this.store.indexOf(v);
14719                 return false;
14720             }
14721             return true;
14722         }, this);
14723         
14724         if (match === false) {
14725             return true; // no more action?
14726         }
14727         // scroll to?
14728         this.view.select(match);
14729         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14730         sn.scrollIntoView(sn.dom.parentNode, false);
14731     },
14732     
14733     onViewScroll : function(e, t){
14734         
14735         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){
14736             return;
14737         }
14738         
14739         this.hasQuery = true;
14740         
14741         this.loading = this.list.select('.loading', true).first();
14742         
14743         if(this.loading === null){
14744             this.list.createChild({
14745                 tag: 'div',
14746                 cls: 'loading roo-select2-more-results roo-select2-active',
14747                 html: 'Loading more results...'
14748             });
14749             
14750             this.loading = this.list.select('.loading', true).first();
14751             
14752             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14753             
14754             this.loading.hide();
14755         }
14756         
14757         this.loading.show();
14758         
14759         var _combo = this;
14760         
14761         this.page++;
14762         this.loadNext = true;
14763         
14764         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14765         
14766         return;
14767     },
14768     
14769     addItem : function(o)
14770     {   
14771         var dv = ''; // display value
14772         
14773         if (this.displayField) {
14774             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14775         } else {
14776             // this is an error condition!!!
14777             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14778         }
14779         
14780         if(!dv.length){
14781             return;
14782         }
14783         
14784         var choice = this.choices.createChild({
14785             tag: 'li',
14786             cls: 'roo-select2-search-choice',
14787             cn: [
14788                 {
14789                     tag: 'div',
14790                     html: dv
14791                 },
14792                 {
14793                     tag: 'a',
14794                     href: '#',
14795                     cls: 'roo-select2-search-choice-close fa fa-times',
14796                     tabindex: '-1'
14797                 }
14798             ]
14799             
14800         }, this.searchField);
14801         
14802         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14803         
14804         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14805         
14806         this.item.push(o);
14807         
14808         this.lastData = o;
14809         
14810         this.syncValue();
14811         
14812         this.inputEl().dom.value = '';
14813         
14814         this.validate();
14815     },
14816     
14817     onRemoveItem : function(e, _self, o)
14818     {
14819         e.preventDefault();
14820         
14821         this.lastItem = Roo.apply([], this.item);
14822         
14823         var index = this.item.indexOf(o.data) * 1;
14824         
14825         if( index < 0){
14826             Roo.log('not this item?!');
14827             return;
14828         }
14829         
14830         this.item.splice(index, 1);
14831         o.item.remove();
14832         
14833         this.syncValue();
14834         
14835         this.fireEvent('remove', this, e);
14836         
14837         this.validate();
14838         
14839     },
14840     
14841     syncValue : function()
14842     {
14843         if(!this.item.length){
14844             this.clearValue();
14845             return;
14846         }
14847             
14848         var value = [];
14849         var _this = this;
14850         Roo.each(this.item, function(i){
14851             if(_this.valueField){
14852                 value.push(i[_this.valueField]);
14853                 return;
14854             }
14855
14856             value.push(i);
14857         });
14858
14859         this.value = value.join(',');
14860
14861         if(this.hiddenField){
14862             this.hiddenField.dom.value = this.value;
14863         }
14864         
14865         this.store.fireEvent("datachanged", this.store);
14866         
14867         this.validate();
14868     },
14869     
14870     clearItem : function()
14871     {
14872         if(!this.multiple){
14873             return;
14874         }
14875         
14876         this.item = [];
14877         
14878         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14879            c.remove();
14880         });
14881         
14882         this.syncValue();
14883         
14884         this.validate();
14885         
14886         if(this.tickable && !Roo.isTouch){
14887             this.view.refresh();
14888         }
14889     },
14890     
14891     inputEl: function ()
14892     {
14893         if(Roo.isIOS && this.useNativeIOS){
14894             return this.el.select('select.roo-ios-select', true).first();
14895         }
14896         
14897         if(Roo.isTouch && this.mobileTouchView){
14898             return this.el.select('input.form-control',true).first();
14899         }
14900         
14901         if(this.tickable){
14902             return this.searchField;
14903         }
14904         
14905         return this.el.select('input.form-control',true).first();
14906     },
14907     
14908     onTickableFooterButtonClick : function(e, btn, el)
14909     {
14910         e.preventDefault();
14911         
14912         this.lastItem = Roo.apply([], this.item);
14913         
14914         if(btn && btn.name == 'cancel'){
14915             this.tickItems = Roo.apply([], this.item);
14916             this.collapse();
14917             return;
14918         }
14919         
14920         this.clearItem();
14921         
14922         var _this = this;
14923         
14924         Roo.each(this.tickItems, function(o){
14925             _this.addItem(o);
14926         });
14927         
14928         this.collapse();
14929         
14930     },
14931     
14932     validate : function()
14933     {
14934         if(this.getVisibilityEl().hasClass('hidden')){
14935             return true;
14936         }
14937         
14938         var v = this.getRawValue();
14939         
14940         if(this.multiple){
14941             v = this.getValue();
14942         }
14943         
14944         if(this.disabled || this.allowBlank || v.length){
14945             this.markValid();
14946             return true;
14947         }
14948         
14949         this.markInvalid();
14950         return false;
14951     },
14952     
14953     tickableInputEl : function()
14954     {
14955         if(!this.tickable || !this.editable){
14956             return this.inputEl();
14957         }
14958         
14959         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14960     },
14961     
14962     
14963     getAutoCreateTouchView : function()
14964     {
14965         var id = Roo.id();
14966         
14967         var cfg = {
14968             cls: 'form-group' //input-group
14969         };
14970         
14971         var input =  {
14972             tag: 'input',
14973             id : id,
14974             type : this.inputType,
14975             cls : 'form-control x-combo-noedit',
14976             autocomplete: 'new-password',
14977             placeholder : this.placeholder || '',
14978             readonly : true
14979         };
14980         
14981         if (this.name) {
14982             input.name = this.name;
14983         }
14984         
14985         if (this.size) {
14986             input.cls += ' input-' + this.size;
14987         }
14988         
14989         if (this.disabled) {
14990             input.disabled = true;
14991         }
14992         
14993         var inputblock = {
14994             cls : '',
14995             cn : [
14996                 input
14997             ]
14998         };
14999         
15000         if(this.before){
15001             inputblock.cls += ' input-group';
15002             
15003             inputblock.cn.unshift({
15004                 tag :'span',
15005                 cls : 'input-group-addon',
15006                 html : this.before
15007             });
15008         }
15009         
15010         if(this.removable && !this.multiple){
15011             inputblock.cls += ' roo-removable';
15012             
15013             inputblock.cn.push({
15014                 tag: 'button',
15015                 html : 'x',
15016                 cls : 'roo-combo-removable-btn close'
15017             });
15018         }
15019
15020         if(this.hasFeedback && !this.allowBlank){
15021             
15022             inputblock.cls += ' has-feedback';
15023             
15024             inputblock.cn.push({
15025                 tag: 'span',
15026                 cls: 'glyphicon form-control-feedback'
15027             });
15028             
15029         }
15030         
15031         if (this.after) {
15032             
15033             inputblock.cls += (this.before) ? '' : ' input-group';
15034             
15035             inputblock.cn.push({
15036                 tag :'span',
15037                 cls : 'input-group-addon',
15038                 html : this.after
15039             });
15040         }
15041
15042         var box = {
15043             tag: 'div',
15044             cn: [
15045                 {
15046                     tag: 'input',
15047                     type : 'hidden',
15048                     cls: 'form-hidden-field'
15049                 },
15050                 inputblock
15051             ]
15052             
15053         };
15054         
15055         if(this.multiple){
15056             box = {
15057                 tag: 'div',
15058                 cn: [
15059                     {
15060                         tag: 'input',
15061                         type : 'hidden',
15062                         cls: 'form-hidden-field'
15063                     },
15064                     {
15065                         tag: 'ul',
15066                         cls: 'roo-select2-choices',
15067                         cn:[
15068                             {
15069                                 tag: 'li',
15070                                 cls: 'roo-select2-search-field',
15071                                 cn: [
15072
15073                                     inputblock
15074                                 ]
15075                             }
15076                         ]
15077                     }
15078                 ]
15079             }
15080         };
15081         
15082         var combobox = {
15083             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15084             cn: [
15085                 box
15086             ]
15087         };
15088         
15089         if(!this.multiple && this.showToggleBtn){
15090             
15091             var caret = {
15092                         tag: 'span',
15093                         cls: 'caret'
15094             };
15095             
15096             if (this.caret != false) {
15097                 caret = {
15098                      tag: 'i',
15099                      cls: 'fa fa-' + this.caret
15100                 };
15101                 
15102             }
15103             
15104             combobox.cn.push({
15105                 tag :'span',
15106                 cls : 'input-group-addon btn dropdown-toggle',
15107                 cn : [
15108                     caret,
15109                     {
15110                         tag: 'span',
15111                         cls: 'combobox-clear',
15112                         cn  : [
15113                             {
15114                                 tag : 'i',
15115                                 cls: 'icon-remove'
15116                             }
15117                         ]
15118                     }
15119                 ]
15120
15121             })
15122         }
15123         
15124         if(this.multiple){
15125             combobox.cls += ' roo-select2-container-multi';
15126         }
15127         
15128         var align = this.labelAlign || this.parentLabelAlign();
15129         
15130         if (align ==='left' && this.fieldLabel.length) {
15131
15132             cfg.cn = [
15133                 {
15134                    tag : 'i',
15135                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15136                    tooltip : 'This field is required'
15137                 },
15138                 {
15139                     tag: 'label',
15140                     cls : 'control-label',
15141                     html : this.fieldLabel
15142
15143                 },
15144                 {
15145                     cls : '', 
15146                     cn: [
15147                         combobox
15148                     ]
15149                 }
15150             ];
15151             
15152             var labelCfg = cfg.cn[1];
15153             var contentCfg = cfg.cn[2];
15154             
15155
15156             if(this.indicatorpos == 'right'){
15157                 cfg.cn = [
15158                     {
15159                         tag: 'label',
15160                         'for' :  id,
15161                         cls : 'control-label',
15162                         cn : [
15163                             {
15164                                 tag : 'span',
15165                                 html : this.fieldLabel
15166                             },
15167                             {
15168                                 tag : 'i',
15169                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15170                                 tooltip : 'This field is required'
15171                             }
15172                         ]
15173                     },
15174                     {
15175                         cls : "",
15176                         cn: [
15177                             combobox
15178                         ]
15179                     }
15180
15181                 ];
15182                 
15183                 labelCfg = cfg.cn[0];
15184                 contentCfg = cfg.cn[1];
15185             }
15186             
15187            
15188             
15189             if(this.labelWidth > 12){
15190                 labelCfg.style = "width: " + this.labelWidth + 'px';
15191             }
15192             
15193             if(this.labelWidth < 13 && this.labelmd == 0){
15194                 this.labelmd = this.labelWidth;
15195             }
15196             
15197             if(this.labellg > 0){
15198                 labelCfg.cls += ' col-lg-' + this.labellg;
15199                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15200             }
15201             
15202             if(this.labelmd > 0){
15203                 labelCfg.cls += ' col-md-' + this.labelmd;
15204                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15205             }
15206             
15207             if(this.labelsm > 0){
15208                 labelCfg.cls += ' col-sm-' + this.labelsm;
15209                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15210             }
15211             
15212             if(this.labelxs > 0){
15213                 labelCfg.cls += ' col-xs-' + this.labelxs;
15214                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15215             }
15216                 
15217                 
15218         } else if ( this.fieldLabel.length) {
15219             cfg.cn = [
15220                 {
15221                    tag : 'i',
15222                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15223                    tooltip : 'This field is required'
15224                 },
15225                 {
15226                     tag: 'label',
15227                     cls : 'control-label',
15228                     html : this.fieldLabel
15229
15230                 },
15231                 {
15232                     cls : '', 
15233                     cn: [
15234                         combobox
15235                     ]
15236                 }
15237             ];
15238             
15239             if(this.indicatorpos == 'right'){
15240                 cfg.cn = [
15241                     {
15242                         tag: 'label',
15243                         cls : 'control-label',
15244                         html : this.fieldLabel,
15245                         cn : [
15246                             {
15247                                tag : 'i',
15248                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15249                                tooltip : 'This field is required'
15250                             }
15251                         ]
15252                     },
15253                     {
15254                         cls : '', 
15255                         cn: [
15256                             combobox
15257                         ]
15258                     }
15259                 ];
15260             }
15261         } else {
15262             cfg.cn = combobox;    
15263         }
15264         
15265         
15266         var settings = this;
15267         
15268         ['xs','sm','md','lg'].map(function(size){
15269             if (settings[size]) {
15270                 cfg.cls += ' col-' + size + '-' + settings[size];
15271             }
15272         });
15273         
15274         return cfg;
15275     },
15276     
15277     initTouchView : function()
15278     {
15279         this.renderTouchView();
15280         
15281         this.touchViewEl.on('scroll', function(){
15282             this.el.dom.scrollTop = 0;
15283         }, this);
15284         
15285         this.originalValue = this.getValue();
15286         
15287         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15288         
15289         this.inputEl().on("click", this.showTouchView, this);
15290         if (this.triggerEl) {
15291             this.triggerEl.on("click", this.showTouchView, this);
15292         }
15293         
15294         
15295         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15296         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15297         
15298         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15299         
15300         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15301         this.store.on('load', this.onTouchViewLoad, this);
15302         this.store.on('loadexception', this.onTouchViewLoadException, this);
15303         
15304         if(this.hiddenName){
15305             
15306             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15307             
15308             this.hiddenField.dom.value =
15309                 this.hiddenValue !== undefined ? this.hiddenValue :
15310                 this.value !== undefined ? this.value : '';
15311         
15312             this.el.dom.removeAttribute('name');
15313             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15314         }
15315         
15316         if(this.multiple){
15317             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15318             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15319         }
15320         
15321         if(this.removable && !this.multiple){
15322             var close = this.closeTriggerEl();
15323             if(close){
15324                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15325                 close.on('click', this.removeBtnClick, this, close);
15326             }
15327         }
15328         /*
15329          * fix the bug in Safari iOS8
15330          */
15331         this.inputEl().on("focus", function(e){
15332             document.activeElement.blur();
15333         }, this);
15334         
15335         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15336         
15337         return;
15338         
15339         
15340     },
15341     
15342     renderTouchView : function()
15343     {
15344         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15345         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15346         
15347         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15348         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15349         
15350         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15351         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15352         this.touchViewBodyEl.setStyle('overflow', 'auto');
15353         
15354         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15355         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15356         
15357         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15358         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15359         
15360     },
15361     
15362     showTouchView : function()
15363     {
15364         if(this.disabled){
15365             return;
15366         }
15367         
15368         this.touchViewHeaderEl.hide();
15369
15370         if(this.modalTitle.length){
15371             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15372             this.touchViewHeaderEl.show();
15373         }
15374
15375         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15376         this.touchViewEl.show();
15377
15378         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15379         
15380         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15381         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15382
15383         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15384
15385         if(this.modalTitle.length){
15386             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15387         }
15388         
15389         this.touchViewBodyEl.setHeight(bodyHeight);
15390
15391         if(this.animate){
15392             var _this = this;
15393             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15394         }else{
15395             this.touchViewEl.addClass('in');
15396         }
15397         
15398         if(this._touchViewMask){
15399             Roo.get(document.body).addClass("x-body-masked");
15400             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15401             this._touchViewMask.setStyle('z-index', 10000);
15402             this._touchViewMask.addClass('show');
15403         }
15404         
15405         this.doTouchViewQuery();
15406         
15407     },
15408     
15409     hideTouchView : function()
15410     {
15411         this.touchViewEl.removeClass('in');
15412
15413         if(this.animate){
15414             var _this = this;
15415             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15416         }else{
15417             this.touchViewEl.setStyle('display', 'none');
15418         }
15419         
15420         if(this._touchViewMask){
15421             this._touchViewMask.removeClass('show');
15422             Roo.get(document.body).removeClass("x-body-masked");
15423         }
15424     },
15425     
15426     setTouchViewValue : function()
15427     {
15428         if(this.multiple){
15429             this.clearItem();
15430         
15431             var _this = this;
15432
15433             Roo.each(this.tickItems, function(o){
15434                 this.addItem(o);
15435             }, this);
15436         }
15437         
15438         this.hideTouchView();
15439     },
15440     
15441     doTouchViewQuery : function()
15442     {
15443         var qe = {
15444             query: '',
15445             forceAll: true,
15446             combo: this,
15447             cancel:false
15448         };
15449         
15450         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15451             return false;
15452         }
15453         
15454         if(!this.alwaysQuery || this.mode == 'local'){
15455             this.onTouchViewLoad();
15456             return;
15457         }
15458         
15459         this.store.load();
15460     },
15461     
15462     onTouchViewBeforeLoad : function(combo,opts)
15463     {
15464         return;
15465     },
15466
15467     // private
15468     onTouchViewLoad : function()
15469     {
15470         if(this.store.getCount() < 1){
15471             this.onTouchViewEmptyResults();
15472             return;
15473         }
15474         
15475         this.clearTouchView();
15476         
15477         var rawValue = this.getRawValue();
15478         
15479         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15480         
15481         this.tickItems = [];
15482         
15483         this.store.data.each(function(d, rowIndex){
15484             var row = this.touchViewListGroup.createChild(template);
15485             
15486             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15487                 row.addClass(d.data.cls);
15488             }
15489             
15490             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15491                 var cfg = {
15492                     data : d.data,
15493                     html : d.data[this.displayField]
15494                 };
15495                 
15496                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15497                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15498                 }
15499             }
15500             row.removeClass('selected');
15501             if(!this.multiple && this.valueField &&
15502                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15503             {
15504                 // radio buttons..
15505                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15506                 row.addClass('selected');
15507             }
15508             
15509             if(this.multiple && this.valueField &&
15510                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15511             {
15512                 
15513                 // checkboxes...
15514                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15515                 this.tickItems.push(d.data);
15516             }
15517             
15518             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15519             
15520         }, this);
15521         
15522         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15523         
15524         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15525
15526         if(this.modalTitle.length){
15527             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15528         }
15529
15530         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15531         
15532         if(this.mobile_restrict_height && listHeight < bodyHeight){
15533             this.touchViewBodyEl.setHeight(listHeight);
15534         }
15535         
15536         var _this = this;
15537         
15538         if(firstChecked && listHeight > bodyHeight){
15539             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15540         }
15541         
15542     },
15543     
15544     onTouchViewLoadException : function()
15545     {
15546         this.hideTouchView();
15547     },
15548     
15549     onTouchViewEmptyResults : function()
15550     {
15551         this.clearTouchView();
15552         
15553         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15554         
15555         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15556         
15557     },
15558     
15559     clearTouchView : function()
15560     {
15561         this.touchViewListGroup.dom.innerHTML = '';
15562     },
15563     
15564     onTouchViewClick : function(e, el, o)
15565     {
15566         e.preventDefault();
15567         
15568         var row = o.row;
15569         var rowIndex = o.rowIndex;
15570         
15571         var r = this.store.getAt(rowIndex);
15572         
15573         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15574             
15575             if(!this.multiple){
15576                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15577                     c.dom.removeAttribute('checked');
15578                 }, this);
15579
15580                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15581
15582                 this.setFromData(r.data);
15583
15584                 var close = this.closeTriggerEl();
15585
15586                 if(close){
15587                     close.show();
15588                 }
15589
15590                 this.hideTouchView();
15591
15592                 this.fireEvent('select', this, r, rowIndex);
15593
15594                 return;
15595             }
15596
15597             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15598                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15599                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15600                 return;
15601             }
15602
15603             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15604             this.addItem(r.data);
15605             this.tickItems.push(r.data);
15606         }
15607     },
15608     
15609     getAutoCreateNativeIOS : function()
15610     {
15611         var cfg = {
15612             cls: 'form-group' //input-group,
15613         };
15614         
15615         var combobox =  {
15616             tag: 'select',
15617             cls : 'roo-ios-select'
15618         };
15619         
15620         if (this.name) {
15621             combobox.name = this.name;
15622         }
15623         
15624         if (this.disabled) {
15625             combobox.disabled = true;
15626         }
15627         
15628         var settings = this;
15629         
15630         ['xs','sm','md','lg'].map(function(size){
15631             if (settings[size]) {
15632                 cfg.cls += ' col-' + size + '-' + settings[size];
15633             }
15634         });
15635         
15636         cfg.cn = combobox;
15637         
15638         return cfg;
15639         
15640     },
15641     
15642     initIOSView : function()
15643     {
15644         this.store.on('load', this.onIOSViewLoad, this);
15645         
15646         return;
15647     },
15648     
15649     onIOSViewLoad : function()
15650     {
15651         if(this.store.getCount() < 1){
15652             return;
15653         }
15654         
15655         this.clearIOSView();
15656         
15657         if(this.allowBlank) {
15658             
15659             var default_text = '-- SELECT --';
15660             
15661             if(this.placeholder.length){
15662                 default_text = this.placeholder;
15663             }
15664             
15665             if(this.emptyTitle.length){
15666                 default_text += ' - ' + this.emptyTitle + ' -';
15667             }
15668             
15669             var opt = this.inputEl().createChild({
15670                 tag: 'option',
15671                 value : 0,
15672                 html : default_text
15673             });
15674             
15675             var o = {};
15676             o[this.valueField] = 0;
15677             o[this.displayField] = default_text;
15678             
15679             this.ios_options.push({
15680                 data : o,
15681                 el : opt
15682             });
15683             
15684         }
15685         
15686         this.store.data.each(function(d, rowIndex){
15687             
15688             var html = '';
15689             
15690             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15691                 html = d.data[this.displayField];
15692             }
15693             
15694             var value = '';
15695             
15696             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15697                 value = d.data[this.valueField];
15698             }
15699             
15700             var option = {
15701                 tag: 'option',
15702                 value : value,
15703                 html : html
15704             };
15705             
15706             if(this.value == d.data[this.valueField]){
15707                 option['selected'] = true;
15708             }
15709             
15710             var opt = this.inputEl().createChild(option);
15711             
15712             this.ios_options.push({
15713                 data : d.data,
15714                 el : opt
15715             });
15716             
15717         }, this);
15718         
15719         this.inputEl().on('change', function(){
15720            this.fireEvent('select', this);
15721         }, this);
15722         
15723     },
15724     
15725     clearIOSView: function()
15726     {
15727         this.inputEl().dom.innerHTML = '';
15728         
15729         this.ios_options = [];
15730     },
15731     
15732     setIOSValue: function(v)
15733     {
15734         this.value = v;
15735         
15736         if(!this.ios_options){
15737             return;
15738         }
15739         
15740         Roo.each(this.ios_options, function(opts){
15741            
15742            opts.el.dom.removeAttribute('selected');
15743            
15744            if(opts.data[this.valueField] != v){
15745                return;
15746            }
15747            
15748            opts.el.dom.setAttribute('selected', true);
15749            
15750         }, this);
15751     }
15752
15753     /** 
15754     * @cfg {Boolean} grow 
15755     * @hide 
15756     */
15757     /** 
15758     * @cfg {Number} growMin 
15759     * @hide 
15760     */
15761     /** 
15762     * @cfg {Number} growMax 
15763     * @hide 
15764     */
15765     /**
15766      * @hide
15767      * @method autoSize
15768      */
15769 });
15770
15771 Roo.apply(Roo.bootstrap.ComboBox,  {
15772     
15773     header : {
15774         tag: 'div',
15775         cls: 'modal-header',
15776         cn: [
15777             {
15778                 tag: 'h4',
15779                 cls: 'modal-title'
15780             }
15781         ]
15782     },
15783     
15784     body : {
15785         tag: 'div',
15786         cls: 'modal-body',
15787         cn: [
15788             {
15789                 tag: 'ul',
15790                 cls: 'list-group'
15791             }
15792         ]
15793     },
15794     
15795     listItemRadio : {
15796         tag: 'li',
15797         cls: 'list-group-item',
15798         cn: [
15799             {
15800                 tag: 'span',
15801                 cls: 'roo-combobox-list-group-item-value'
15802             },
15803             {
15804                 tag: 'div',
15805                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15806                 cn: [
15807                     {
15808                         tag: 'input',
15809                         type: 'radio'
15810                     },
15811                     {
15812                         tag: 'label'
15813                     }
15814                 ]
15815             }
15816         ]
15817     },
15818     
15819     listItemCheckbox : {
15820         tag: 'li',
15821         cls: 'list-group-item',
15822         cn: [
15823             {
15824                 tag: 'span',
15825                 cls: 'roo-combobox-list-group-item-value'
15826             },
15827             {
15828                 tag: 'div',
15829                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15830                 cn: [
15831                     {
15832                         tag: 'input',
15833                         type: 'checkbox'
15834                     },
15835                     {
15836                         tag: 'label'
15837                     }
15838                 ]
15839             }
15840         ]
15841     },
15842     
15843     emptyResult : {
15844         tag: 'div',
15845         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15846     },
15847     
15848     footer : {
15849         tag: 'div',
15850         cls: 'modal-footer',
15851         cn: [
15852             {
15853                 tag: 'div',
15854                 cls: 'row',
15855                 cn: [
15856                     {
15857                         tag: 'div',
15858                         cls: 'col-xs-6 text-left',
15859                         cn: {
15860                             tag: 'button',
15861                             cls: 'btn btn-danger roo-touch-view-cancel',
15862                             html: 'Cancel'
15863                         }
15864                     },
15865                     {
15866                         tag: 'div',
15867                         cls: 'col-xs-6 text-right',
15868                         cn: {
15869                             tag: 'button',
15870                             cls: 'btn btn-success roo-touch-view-ok',
15871                             html: 'OK'
15872                         }
15873                     }
15874                 ]
15875             }
15876         ]
15877         
15878     }
15879 });
15880
15881 Roo.apply(Roo.bootstrap.ComboBox,  {
15882     
15883     touchViewTemplate : {
15884         tag: 'div',
15885         cls: 'modal fade roo-combobox-touch-view',
15886         cn: [
15887             {
15888                 tag: 'div',
15889                 cls: 'modal-dialog',
15890                 style : 'position:fixed', // we have to fix position....
15891                 cn: [
15892                     {
15893                         tag: 'div',
15894                         cls: 'modal-content',
15895                         cn: [
15896                             Roo.bootstrap.ComboBox.header,
15897                             Roo.bootstrap.ComboBox.body,
15898                             Roo.bootstrap.ComboBox.footer
15899                         ]
15900                     }
15901                 ]
15902             }
15903         ]
15904     }
15905 });/*
15906  * Based on:
15907  * Ext JS Library 1.1.1
15908  * Copyright(c) 2006-2007, Ext JS, LLC.
15909  *
15910  * Originally Released Under LGPL - original licence link has changed is not relivant.
15911  *
15912  * Fork - LGPL
15913  * <script type="text/javascript">
15914  */
15915
15916 /**
15917  * @class Roo.View
15918  * @extends Roo.util.Observable
15919  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15920  * This class also supports single and multi selection modes. <br>
15921  * Create a data model bound view:
15922  <pre><code>
15923  var store = new Roo.data.Store(...);
15924
15925  var view = new Roo.View({
15926     el : "my-element",
15927     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15928  
15929     singleSelect: true,
15930     selectedClass: "ydataview-selected",
15931     store: store
15932  });
15933
15934  // listen for node click?
15935  view.on("click", function(vw, index, node, e){
15936  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15937  });
15938
15939  // load XML data
15940  dataModel.load("foobar.xml");
15941  </code></pre>
15942  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15943  * <br><br>
15944  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15945  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15946  * 
15947  * Note: old style constructor is still suported (container, template, config)
15948  * 
15949  * @constructor
15950  * Create a new View
15951  * @param {Object} config The config object
15952  * 
15953  */
15954 Roo.View = function(config, depreciated_tpl, depreciated_config){
15955     
15956     this.parent = false;
15957     
15958     if (typeof(depreciated_tpl) == 'undefined') {
15959         // new way.. - universal constructor.
15960         Roo.apply(this, config);
15961         this.el  = Roo.get(this.el);
15962     } else {
15963         // old format..
15964         this.el  = Roo.get(config);
15965         this.tpl = depreciated_tpl;
15966         Roo.apply(this, depreciated_config);
15967     }
15968     this.wrapEl  = this.el.wrap().wrap();
15969     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15970     
15971     
15972     if(typeof(this.tpl) == "string"){
15973         this.tpl = new Roo.Template(this.tpl);
15974     } else {
15975         // support xtype ctors..
15976         this.tpl = new Roo.factory(this.tpl, Roo);
15977     }
15978     
15979     
15980     this.tpl.compile();
15981     
15982     /** @private */
15983     this.addEvents({
15984         /**
15985          * @event beforeclick
15986          * Fires before a click is processed. Returns false to cancel the default action.
15987          * @param {Roo.View} this
15988          * @param {Number} index The index of the target node
15989          * @param {HTMLElement} node The target node
15990          * @param {Roo.EventObject} e The raw event object
15991          */
15992             "beforeclick" : true,
15993         /**
15994          * @event click
15995          * Fires when a template node is clicked.
15996          * @param {Roo.View} this
15997          * @param {Number} index The index of the target node
15998          * @param {HTMLElement} node The target node
15999          * @param {Roo.EventObject} e The raw event object
16000          */
16001             "click" : true,
16002         /**
16003          * @event dblclick
16004          * Fires when a template node is double clicked.
16005          * @param {Roo.View} this
16006          * @param {Number} index The index of the target node
16007          * @param {HTMLElement} node The target node
16008          * @param {Roo.EventObject} e The raw event object
16009          */
16010             "dblclick" : true,
16011         /**
16012          * @event contextmenu
16013          * Fires when a template node is right clicked.
16014          * @param {Roo.View} this
16015          * @param {Number} index The index of the target node
16016          * @param {HTMLElement} node The target node
16017          * @param {Roo.EventObject} e The raw event object
16018          */
16019             "contextmenu" : true,
16020         /**
16021          * @event selectionchange
16022          * Fires when the selected nodes change.
16023          * @param {Roo.View} this
16024          * @param {Array} selections Array of the selected nodes
16025          */
16026             "selectionchange" : true,
16027     
16028         /**
16029          * @event beforeselect
16030          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16031          * @param {Roo.View} this
16032          * @param {HTMLElement} node The node to be selected
16033          * @param {Array} selections Array of currently selected nodes
16034          */
16035             "beforeselect" : true,
16036         /**
16037          * @event preparedata
16038          * Fires on every row to render, to allow you to change the data.
16039          * @param {Roo.View} this
16040          * @param {Object} data to be rendered (change this)
16041          */
16042           "preparedata" : true
16043           
16044           
16045         });
16046
16047
16048
16049     this.el.on({
16050         "click": this.onClick,
16051         "dblclick": this.onDblClick,
16052         "contextmenu": this.onContextMenu,
16053         scope:this
16054     });
16055
16056     this.selections = [];
16057     this.nodes = [];
16058     this.cmp = new Roo.CompositeElementLite([]);
16059     if(this.store){
16060         this.store = Roo.factory(this.store, Roo.data);
16061         this.setStore(this.store, true);
16062     }
16063     
16064     if ( this.footer && this.footer.xtype) {
16065            
16066          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16067         
16068         this.footer.dataSource = this.store;
16069         this.footer.container = fctr;
16070         this.footer = Roo.factory(this.footer, Roo);
16071         fctr.insertFirst(this.el);
16072         
16073         // this is a bit insane - as the paging toolbar seems to detach the el..
16074 //        dom.parentNode.parentNode.parentNode
16075          // they get detached?
16076     }
16077     
16078     
16079     Roo.View.superclass.constructor.call(this);
16080     
16081     
16082 };
16083
16084 Roo.extend(Roo.View, Roo.util.Observable, {
16085     
16086      /**
16087      * @cfg {Roo.data.Store} store Data store to load data from.
16088      */
16089     store : false,
16090     
16091     /**
16092      * @cfg {String|Roo.Element} el The container element.
16093      */
16094     el : '',
16095     
16096     /**
16097      * @cfg {String|Roo.Template} tpl The template used by this View 
16098      */
16099     tpl : false,
16100     /**
16101      * @cfg {String} dataName the named area of the template to use as the data area
16102      *                          Works with domtemplates roo-name="name"
16103      */
16104     dataName: false,
16105     /**
16106      * @cfg {String} selectedClass The css class to add to selected nodes
16107      */
16108     selectedClass : "x-view-selected",
16109      /**
16110      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16111      */
16112     emptyText : "",
16113     
16114     /**
16115      * @cfg {String} text to display on mask (default Loading)
16116      */
16117     mask : false,
16118     /**
16119      * @cfg {Boolean} multiSelect Allow multiple selection
16120      */
16121     multiSelect : false,
16122     /**
16123      * @cfg {Boolean} singleSelect Allow single selection
16124      */
16125     singleSelect:  false,
16126     
16127     /**
16128      * @cfg {Boolean} toggleSelect - selecting 
16129      */
16130     toggleSelect : false,
16131     
16132     /**
16133      * @cfg {Boolean} tickable - selecting 
16134      */
16135     tickable : false,
16136     
16137     /**
16138      * Returns the element this view is bound to.
16139      * @return {Roo.Element}
16140      */
16141     getEl : function(){
16142         return this.wrapEl;
16143     },
16144     
16145     
16146
16147     /**
16148      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16149      */
16150     refresh : function(){
16151         //Roo.log('refresh');
16152         var t = this.tpl;
16153         
16154         // if we are using something like 'domtemplate', then
16155         // the what gets used is:
16156         // t.applySubtemplate(NAME, data, wrapping data..)
16157         // the outer template then get' applied with
16158         //     the store 'extra data'
16159         // and the body get's added to the
16160         //      roo-name="data" node?
16161         //      <span class='roo-tpl-{name}'></span> ?????
16162         
16163         
16164         
16165         this.clearSelections();
16166         this.el.update("");
16167         var html = [];
16168         var records = this.store.getRange();
16169         if(records.length < 1) {
16170             
16171             // is this valid??  = should it render a template??
16172             
16173             this.el.update(this.emptyText);
16174             return;
16175         }
16176         var el = this.el;
16177         if (this.dataName) {
16178             this.el.update(t.apply(this.store.meta)); //????
16179             el = this.el.child('.roo-tpl-' + this.dataName);
16180         }
16181         
16182         for(var i = 0, len = records.length; i < len; i++){
16183             var data = this.prepareData(records[i].data, i, records[i]);
16184             this.fireEvent("preparedata", this, data, i, records[i]);
16185             
16186             var d = Roo.apply({}, data);
16187             
16188             if(this.tickable){
16189                 Roo.apply(d, {'roo-id' : Roo.id()});
16190                 
16191                 var _this = this;
16192             
16193                 Roo.each(this.parent.item, function(item){
16194                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16195                         return;
16196                     }
16197                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16198                 });
16199             }
16200             
16201             html[html.length] = Roo.util.Format.trim(
16202                 this.dataName ?
16203                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16204                     t.apply(d)
16205             );
16206         }
16207         
16208         
16209         
16210         el.update(html.join(""));
16211         this.nodes = el.dom.childNodes;
16212         this.updateIndexes(0);
16213     },
16214     
16215
16216     /**
16217      * Function to override to reformat the data that is sent to
16218      * the template for each node.
16219      * DEPRICATED - use the preparedata event handler.
16220      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16221      * a JSON object for an UpdateManager bound view).
16222      */
16223     prepareData : function(data, index, record)
16224     {
16225         this.fireEvent("preparedata", this, data, index, record);
16226         return data;
16227     },
16228
16229     onUpdate : function(ds, record){
16230         // Roo.log('on update');   
16231         this.clearSelections();
16232         var index = this.store.indexOf(record);
16233         var n = this.nodes[index];
16234         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16235         n.parentNode.removeChild(n);
16236         this.updateIndexes(index, index);
16237     },
16238
16239     
16240     
16241 // --------- FIXME     
16242     onAdd : function(ds, records, index)
16243     {
16244         //Roo.log(['on Add', ds, records, index] );        
16245         this.clearSelections();
16246         if(this.nodes.length == 0){
16247             this.refresh();
16248             return;
16249         }
16250         var n = this.nodes[index];
16251         for(var i = 0, len = records.length; i < len; i++){
16252             var d = this.prepareData(records[i].data, i, records[i]);
16253             if(n){
16254                 this.tpl.insertBefore(n, d);
16255             }else{
16256                 
16257                 this.tpl.append(this.el, d);
16258             }
16259         }
16260         this.updateIndexes(index);
16261     },
16262
16263     onRemove : function(ds, record, index){
16264        // Roo.log('onRemove');
16265         this.clearSelections();
16266         var el = this.dataName  ?
16267             this.el.child('.roo-tpl-' + this.dataName) :
16268             this.el; 
16269         
16270         el.dom.removeChild(this.nodes[index]);
16271         this.updateIndexes(index);
16272     },
16273
16274     /**
16275      * Refresh an individual node.
16276      * @param {Number} index
16277      */
16278     refreshNode : function(index){
16279         this.onUpdate(this.store, this.store.getAt(index));
16280     },
16281
16282     updateIndexes : function(startIndex, endIndex){
16283         var ns = this.nodes;
16284         startIndex = startIndex || 0;
16285         endIndex = endIndex || ns.length - 1;
16286         for(var i = startIndex; i <= endIndex; i++){
16287             ns[i].nodeIndex = i;
16288         }
16289     },
16290
16291     /**
16292      * Changes the data store this view uses and refresh the view.
16293      * @param {Store} store
16294      */
16295     setStore : function(store, initial){
16296         if(!initial && this.store){
16297             this.store.un("datachanged", this.refresh);
16298             this.store.un("add", this.onAdd);
16299             this.store.un("remove", this.onRemove);
16300             this.store.un("update", this.onUpdate);
16301             this.store.un("clear", this.refresh);
16302             this.store.un("beforeload", this.onBeforeLoad);
16303             this.store.un("load", this.onLoad);
16304             this.store.un("loadexception", this.onLoad);
16305         }
16306         if(store){
16307           
16308             store.on("datachanged", this.refresh, this);
16309             store.on("add", this.onAdd, this);
16310             store.on("remove", this.onRemove, this);
16311             store.on("update", this.onUpdate, this);
16312             store.on("clear", this.refresh, this);
16313             store.on("beforeload", this.onBeforeLoad, this);
16314             store.on("load", this.onLoad, this);
16315             store.on("loadexception", this.onLoad, this);
16316         }
16317         
16318         if(store){
16319             this.refresh();
16320         }
16321     },
16322     /**
16323      * onbeforeLoad - masks the loading area.
16324      *
16325      */
16326     onBeforeLoad : function(store,opts)
16327     {
16328          //Roo.log('onBeforeLoad');   
16329         if (!opts.add) {
16330             this.el.update("");
16331         }
16332         this.el.mask(this.mask ? this.mask : "Loading" ); 
16333     },
16334     onLoad : function ()
16335     {
16336         this.el.unmask();
16337     },
16338     
16339
16340     /**
16341      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16342      * @param {HTMLElement} node
16343      * @return {HTMLElement} The template node
16344      */
16345     findItemFromChild : function(node){
16346         var el = this.dataName  ?
16347             this.el.child('.roo-tpl-' + this.dataName,true) :
16348             this.el.dom; 
16349         
16350         if(!node || node.parentNode == el){
16351                     return node;
16352             }
16353             var p = node.parentNode;
16354             while(p && p != el){
16355             if(p.parentNode == el){
16356                 return p;
16357             }
16358             p = p.parentNode;
16359         }
16360             return null;
16361     },
16362
16363     /** @ignore */
16364     onClick : function(e){
16365         var item = this.findItemFromChild(e.getTarget());
16366         if(item){
16367             var index = this.indexOf(item);
16368             if(this.onItemClick(item, index, e) !== false){
16369                 this.fireEvent("click", this, index, item, e);
16370             }
16371         }else{
16372             this.clearSelections();
16373         }
16374     },
16375
16376     /** @ignore */
16377     onContextMenu : function(e){
16378         var item = this.findItemFromChild(e.getTarget());
16379         if(item){
16380             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16381         }
16382     },
16383
16384     /** @ignore */
16385     onDblClick : function(e){
16386         var item = this.findItemFromChild(e.getTarget());
16387         if(item){
16388             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16389         }
16390     },
16391
16392     onItemClick : function(item, index, e)
16393     {
16394         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16395             return false;
16396         }
16397         if (this.toggleSelect) {
16398             var m = this.isSelected(item) ? 'unselect' : 'select';
16399             //Roo.log(m);
16400             var _t = this;
16401             _t[m](item, true, false);
16402             return true;
16403         }
16404         if(this.multiSelect || this.singleSelect){
16405             if(this.multiSelect && e.shiftKey && this.lastSelection){
16406                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16407             }else{
16408                 this.select(item, this.multiSelect && e.ctrlKey);
16409                 this.lastSelection = item;
16410             }
16411             
16412             if(!this.tickable){
16413                 e.preventDefault();
16414             }
16415             
16416         }
16417         return true;
16418     },
16419
16420     /**
16421      * Get the number of selected nodes.
16422      * @return {Number}
16423      */
16424     getSelectionCount : function(){
16425         return this.selections.length;
16426     },
16427
16428     /**
16429      * Get the currently selected nodes.
16430      * @return {Array} An array of HTMLElements
16431      */
16432     getSelectedNodes : function(){
16433         return this.selections;
16434     },
16435
16436     /**
16437      * Get the indexes of the selected nodes.
16438      * @return {Array}
16439      */
16440     getSelectedIndexes : function(){
16441         var indexes = [], s = this.selections;
16442         for(var i = 0, len = s.length; i < len; i++){
16443             indexes.push(s[i].nodeIndex);
16444         }
16445         return indexes;
16446     },
16447
16448     /**
16449      * Clear all selections
16450      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16451      */
16452     clearSelections : function(suppressEvent){
16453         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16454             this.cmp.elements = this.selections;
16455             this.cmp.removeClass(this.selectedClass);
16456             this.selections = [];
16457             if(!suppressEvent){
16458                 this.fireEvent("selectionchange", this, this.selections);
16459             }
16460         }
16461     },
16462
16463     /**
16464      * Returns true if the passed node is selected
16465      * @param {HTMLElement/Number} node The node or node index
16466      * @return {Boolean}
16467      */
16468     isSelected : function(node){
16469         var s = this.selections;
16470         if(s.length < 1){
16471             return false;
16472         }
16473         node = this.getNode(node);
16474         return s.indexOf(node) !== -1;
16475     },
16476
16477     /**
16478      * Selects nodes.
16479      * @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
16480      * @param {Boolean} keepExisting (optional) true to keep existing selections
16481      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16482      */
16483     select : function(nodeInfo, keepExisting, suppressEvent){
16484         if(nodeInfo instanceof Array){
16485             if(!keepExisting){
16486                 this.clearSelections(true);
16487             }
16488             for(var i = 0, len = nodeInfo.length; i < len; i++){
16489                 this.select(nodeInfo[i], true, true);
16490             }
16491             return;
16492         } 
16493         var node = this.getNode(nodeInfo);
16494         if(!node || this.isSelected(node)){
16495             return; // already selected.
16496         }
16497         if(!keepExisting){
16498             this.clearSelections(true);
16499         }
16500         
16501         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16502             Roo.fly(node).addClass(this.selectedClass);
16503             this.selections.push(node);
16504             if(!suppressEvent){
16505                 this.fireEvent("selectionchange", this, this.selections);
16506             }
16507         }
16508         
16509         
16510     },
16511       /**
16512      * Unselects nodes.
16513      * @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
16514      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16515      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16516      */
16517     unselect : function(nodeInfo, keepExisting, suppressEvent)
16518     {
16519         if(nodeInfo instanceof Array){
16520             Roo.each(this.selections, function(s) {
16521                 this.unselect(s, nodeInfo);
16522             }, this);
16523             return;
16524         }
16525         var node = this.getNode(nodeInfo);
16526         if(!node || !this.isSelected(node)){
16527             //Roo.log("not selected");
16528             return; // not selected.
16529         }
16530         // fireevent???
16531         var ns = [];
16532         Roo.each(this.selections, function(s) {
16533             if (s == node ) {
16534                 Roo.fly(node).removeClass(this.selectedClass);
16535
16536                 return;
16537             }
16538             ns.push(s);
16539         },this);
16540         
16541         this.selections= ns;
16542         this.fireEvent("selectionchange", this, this.selections);
16543     },
16544
16545     /**
16546      * Gets a template node.
16547      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16548      * @return {HTMLElement} The node or null if it wasn't found
16549      */
16550     getNode : function(nodeInfo){
16551         if(typeof nodeInfo == "string"){
16552             return document.getElementById(nodeInfo);
16553         }else if(typeof nodeInfo == "number"){
16554             return this.nodes[nodeInfo];
16555         }
16556         return nodeInfo;
16557     },
16558
16559     /**
16560      * Gets a range template nodes.
16561      * @param {Number} startIndex
16562      * @param {Number} endIndex
16563      * @return {Array} An array of nodes
16564      */
16565     getNodes : function(start, end){
16566         var ns = this.nodes;
16567         start = start || 0;
16568         end = typeof end == "undefined" ? ns.length - 1 : end;
16569         var nodes = [];
16570         if(start <= end){
16571             for(var i = start; i <= end; i++){
16572                 nodes.push(ns[i]);
16573             }
16574         } else{
16575             for(var i = start; i >= end; i--){
16576                 nodes.push(ns[i]);
16577             }
16578         }
16579         return nodes;
16580     },
16581
16582     /**
16583      * Finds the index of the passed node
16584      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16585      * @return {Number} The index of the node or -1
16586      */
16587     indexOf : function(node){
16588         node = this.getNode(node);
16589         if(typeof node.nodeIndex == "number"){
16590             return node.nodeIndex;
16591         }
16592         var ns = this.nodes;
16593         for(var i = 0, len = ns.length; i < len; i++){
16594             if(ns[i] == node){
16595                 return i;
16596             }
16597         }
16598         return -1;
16599     }
16600 });
16601 /*
16602  * - LGPL
16603  *
16604  * based on jquery fullcalendar
16605  * 
16606  */
16607
16608 Roo.bootstrap = Roo.bootstrap || {};
16609 /**
16610  * @class Roo.bootstrap.Calendar
16611  * @extends Roo.bootstrap.Component
16612  * Bootstrap Calendar class
16613  * @cfg {Boolean} loadMask (true|false) default false
16614  * @cfg {Object} header generate the user specific header of the calendar, default false
16615
16616  * @constructor
16617  * Create a new Container
16618  * @param {Object} config The config object
16619  */
16620
16621
16622
16623 Roo.bootstrap.Calendar = function(config){
16624     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16625      this.addEvents({
16626         /**
16627              * @event select
16628              * Fires when a date is selected
16629              * @param {DatePicker} this
16630              * @param {Date} date The selected date
16631              */
16632         'select': true,
16633         /**
16634              * @event monthchange
16635              * Fires when the displayed month changes 
16636              * @param {DatePicker} this
16637              * @param {Date} date The selected month
16638              */
16639         'monthchange': true,
16640         /**
16641              * @event evententer
16642              * Fires when mouse over an event
16643              * @param {Calendar} this
16644              * @param {event} Event
16645              */
16646         'evententer': true,
16647         /**
16648              * @event eventleave
16649              * Fires when the mouse leaves an
16650              * @param {Calendar} this
16651              * @param {event}
16652              */
16653         'eventleave': true,
16654         /**
16655              * @event eventclick
16656              * Fires when the mouse click an
16657              * @param {Calendar} this
16658              * @param {event}
16659              */
16660         'eventclick': true
16661         
16662     });
16663
16664 };
16665
16666 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16667     
16668      /**
16669      * @cfg {Number} startDay
16670      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16671      */
16672     startDay : 0,
16673     
16674     loadMask : false,
16675     
16676     header : false,
16677       
16678     getAutoCreate : function(){
16679         
16680         
16681         var fc_button = function(name, corner, style, content ) {
16682             return Roo.apply({},{
16683                 tag : 'span',
16684                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16685                          (corner.length ?
16686                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16687                             ''
16688                         ),
16689                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16690                 unselectable: 'on'
16691             });
16692         };
16693         
16694         var header = {};
16695         
16696         if(!this.header){
16697             header = {
16698                 tag : 'table',
16699                 cls : 'fc-header',
16700                 style : 'width:100%',
16701                 cn : [
16702                     {
16703                         tag: 'tr',
16704                         cn : [
16705                             {
16706                                 tag : 'td',
16707                                 cls : 'fc-header-left',
16708                                 cn : [
16709                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16710                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16711                                     { tag: 'span', cls: 'fc-header-space' },
16712                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16713
16714
16715                                 ]
16716                             },
16717
16718                             {
16719                                 tag : 'td',
16720                                 cls : 'fc-header-center',
16721                                 cn : [
16722                                     {
16723                                         tag: 'span',
16724                                         cls: 'fc-header-title',
16725                                         cn : {
16726                                             tag: 'H2',
16727                                             html : 'month / year'
16728                                         }
16729                                     }
16730
16731                                 ]
16732                             },
16733                             {
16734                                 tag : 'td',
16735                                 cls : 'fc-header-right',
16736                                 cn : [
16737                               /*      fc_button('month', 'left', '', 'month' ),
16738                                     fc_button('week', '', '', 'week' ),
16739                                     fc_button('day', 'right', '', 'day' )
16740                                 */    
16741
16742                                 ]
16743                             }
16744
16745                         ]
16746                     }
16747                 ]
16748             };
16749         }
16750         
16751         header = this.header;
16752         
16753        
16754         var cal_heads = function() {
16755             var ret = [];
16756             // fixme - handle this.
16757             
16758             for (var i =0; i < Date.dayNames.length; i++) {
16759                 var d = Date.dayNames[i];
16760                 ret.push({
16761                     tag: 'th',
16762                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16763                     html : d.substring(0,3)
16764                 });
16765                 
16766             }
16767             ret[0].cls += ' fc-first';
16768             ret[6].cls += ' fc-last';
16769             return ret;
16770         };
16771         var cal_cell = function(n) {
16772             return  {
16773                 tag: 'td',
16774                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16775                 cn : [
16776                     {
16777                         cn : [
16778                             {
16779                                 cls: 'fc-day-number',
16780                                 html: 'D'
16781                             },
16782                             {
16783                                 cls: 'fc-day-content',
16784                              
16785                                 cn : [
16786                                      {
16787                                         style: 'position: relative;' // height: 17px;
16788                                     }
16789                                 ]
16790                             }
16791                             
16792                             
16793                         ]
16794                     }
16795                 ]
16796                 
16797             }
16798         };
16799         var cal_rows = function() {
16800             
16801             var ret = [];
16802             for (var r = 0; r < 6; r++) {
16803                 var row= {
16804                     tag : 'tr',
16805                     cls : 'fc-week',
16806                     cn : []
16807                 };
16808                 
16809                 for (var i =0; i < Date.dayNames.length; i++) {
16810                     var d = Date.dayNames[i];
16811                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16812
16813                 }
16814                 row.cn[0].cls+=' fc-first';
16815                 row.cn[0].cn[0].style = 'min-height:90px';
16816                 row.cn[6].cls+=' fc-last';
16817                 ret.push(row);
16818                 
16819             }
16820             ret[0].cls += ' fc-first';
16821             ret[4].cls += ' fc-prev-last';
16822             ret[5].cls += ' fc-last';
16823             return ret;
16824             
16825         };
16826         
16827         var cal_table = {
16828             tag: 'table',
16829             cls: 'fc-border-separate',
16830             style : 'width:100%',
16831             cellspacing  : 0,
16832             cn : [
16833                 { 
16834                     tag: 'thead',
16835                     cn : [
16836                         { 
16837                             tag: 'tr',
16838                             cls : 'fc-first fc-last',
16839                             cn : cal_heads()
16840                         }
16841                     ]
16842                 },
16843                 { 
16844                     tag: 'tbody',
16845                     cn : cal_rows()
16846                 }
16847                   
16848             ]
16849         };
16850          
16851          var cfg = {
16852             cls : 'fc fc-ltr',
16853             cn : [
16854                 header,
16855                 {
16856                     cls : 'fc-content',
16857                     style : "position: relative;",
16858                     cn : [
16859                         {
16860                             cls : 'fc-view fc-view-month fc-grid',
16861                             style : 'position: relative',
16862                             unselectable : 'on',
16863                             cn : [
16864                                 {
16865                                     cls : 'fc-event-container',
16866                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16867                                 },
16868                                 cal_table
16869                             ]
16870                         }
16871                     ]
16872     
16873                 }
16874            ] 
16875             
16876         };
16877         
16878          
16879         
16880         return cfg;
16881     },
16882     
16883     
16884     initEvents : function()
16885     {
16886         if(!this.store){
16887             throw "can not find store for calendar";
16888         }
16889         
16890         var mark = {
16891             tag: "div",
16892             cls:"x-dlg-mask",
16893             style: "text-align:center",
16894             cn: [
16895                 {
16896                     tag: "div",
16897                     style: "background-color:white;width:50%;margin:250 auto",
16898                     cn: [
16899                         {
16900                             tag: "img",
16901                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16902                         },
16903                         {
16904                             tag: "span",
16905                             html: "Loading"
16906                         }
16907                         
16908                     ]
16909                 }
16910             ]
16911         };
16912         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16913         
16914         var size = this.el.select('.fc-content', true).first().getSize();
16915         this.maskEl.setSize(size.width, size.height);
16916         this.maskEl.enableDisplayMode("block");
16917         if(!this.loadMask){
16918             this.maskEl.hide();
16919         }
16920         
16921         this.store = Roo.factory(this.store, Roo.data);
16922         this.store.on('load', this.onLoad, this);
16923         this.store.on('beforeload', this.onBeforeLoad, this);
16924         
16925         this.resize();
16926         
16927         this.cells = this.el.select('.fc-day',true);
16928         //Roo.log(this.cells);
16929         this.textNodes = this.el.query('.fc-day-number');
16930         this.cells.addClassOnOver('fc-state-hover');
16931         
16932         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16933         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16934         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16935         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16936         
16937         this.on('monthchange', this.onMonthChange, this);
16938         
16939         this.update(new Date().clearTime());
16940     },
16941     
16942     resize : function() {
16943         var sz  = this.el.getSize();
16944         
16945         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16946         this.el.select('.fc-day-content div',true).setHeight(34);
16947     },
16948     
16949     
16950     // private
16951     showPrevMonth : function(e){
16952         this.update(this.activeDate.add("mo", -1));
16953     },
16954     showToday : function(e){
16955         this.update(new Date().clearTime());
16956     },
16957     // private
16958     showNextMonth : function(e){
16959         this.update(this.activeDate.add("mo", 1));
16960     },
16961
16962     // private
16963     showPrevYear : function(){
16964         this.update(this.activeDate.add("y", -1));
16965     },
16966
16967     // private
16968     showNextYear : function(){
16969         this.update(this.activeDate.add("y", 1));
16970     },
16971
16972     
16973    // private
16974     update : function(date)
16975     {
16976         var vd = this.activeDate;
16977         this.activeDate = date;
16978 //        if(vd && this.el){
16979 //            var t = date.getTime();
16980 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16981 //                Roo.log('using add remove');
16982 //                
16983 //                this.fireEvent('monthchange', this, date);
16984 //                
16985 //                this.cells.removeClass("fc-state-highlight");
16986 //                this.cells.each(function(c){
16987 //                   if(c.dateValue == t){
16988 //                       c.addClass("fc-state-highlight");
16989 //                       setTimeout(function(){
16990 //                            try{c.dom.firstChild.focus();}catch(e){}
16991 //                       }, 50);
16992 //                       return false;
16993 //                   }
16994 //                   return true;
16995 //                });
16996 //                return;
16997 //            }
16998 //        }
16999         
17000         var days = date.getDaysInMonth();
17001         
17002         var firstOfMonth = date.getFirstDateOfMonth();
17003         var startingPos = firstOfMonth.getDay()-this.startDay;
17004         
17005         if(startingPos < this.startDay){
17006             startingPos += 7;
17007         }
17008         
17009         var pm = date.add(Date.MONTH, -1);
17010         var prevStart = pm.getDaysInMonth()-startingPos;
17011 //        
17012         this.cells = this.el.select('.fc-day',true);
17013         this.textNodes = this.el.query('.fc-day-number');
17014         this.cells.addClassOnOver('fc-state-hover');
17015         
17016         var cells = this.cells.elements;
17017         var textEls = this.textNodes;
17018         
17019         Roo.each(cells, function(cell){
17020             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17021         });
17022         
17023         days += startingPos;
17024
17025         // convert everything to numbers so it's fast
17026         var day = 86400000;
17027         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17028         //Roo.log(d);
17029         //Roo.log(pm);
17030         //Roo.log(prevStart);
17031         
17032         var today = new Date().clearTime().getTime();
17033         var sel = date.clearTime().getTime();
17034         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17035         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17036         var ddMatch = this.disabledDatesRE;
17037         var ddText = this.disabledDatesText;
17038         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17039         var ddaysText = this.disabledDaysText;
17040         var format = this.format;
17041         
17042         var setCellClass = function(cal, cell){
17043             cell.row = 0;
17044             cell.events = [];
17045             cell.more = [];
17046             //Roo.log('set Cell Class');
17047             cell.title = "";
17048             var t = d.getTime();
17049             
17050             //Roo.log(d);
17051             
17052             cell.dateValue = t;
17053             if(t == today){
17054                 cell.className += " fc-today";
17055                 cell.className += " fc-state-highlight";
17056                 cell.title = cal.todayText;
17057             }
17058             if(t == sel){
17059                 // disable highlight in other month..
17060                 //cell.className += " fc-state-highlight";
17061                 
17062             }
17063             // disabling
17064             if(t < min) {
17065                 cell.className = " fc-state-disabled";
17066                 cell.title = cal.minText;
17067                 return;
17068             }
17069             if(t > max) {
17070                 cell.className = " fc-state-disabled";
17071                 cell.title = cal.maxText;
17072                 return;
17073             }
17074             if(ddays){
17075                 if(ddays.indexOf(d.getDay()) != -1){
17076                     cell.title = ddaysText;
17077                     cell.className = " fc-state-disabled";
17078                 }
17079             }
17080             if(ddMatch && format){
17081                 var fvalue = d.dateFormat(format);
17082                 if(ddMatch.test(fvalue)){
17083                     cell.title = ddText.replace("%0", fvalue);
17084                     cell.className = " fc-state-disabled";
17085                 }
17086             }
17087             
17088             if (!cell.initialClassName) {
17089                 cell.initialClassName = cell.dom.className;
17090             }
17091             
17092             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17093         };
17094
17095         var i = 0;
17096         
17097         for(; i < startingPos; i++) {
17098             textEls[i].innerHTML = (++prevStart);
17099             d.setDate(d.getDate()+1);
17100             
17101             cells[i].className = "fc-past fc-other-month";
17102             setCellClass(this, cells[i]);
17103         }
17104         
17105         var intDay = 0;
17106         
17107         for(; i < days; i++){
17108             intDay = i - startingPos + 1;
17109             textEls[i].innerHTML = (intDay);
17110             d.setDate(d.getDate()+1);
17111             
17112             cells[i].className = ''; // "x-date-active";
17113             setCellClass(this, cells[i]);
17114         }
17115         var extraDays = 0;
17116         
17117         for(; i < 42; i++) {
17118             textEls[i].innerHTML = (++extraDays);
17119             d.setDate(d.getDate()+1);
17120             
17121             cells[i].className = "fc-future fc-other-month";
17122             setCellClass(this, cells[i]);
17123         }
17124         
17125         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17126         
17127         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17128         
17129         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17130         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17131         
17132         if(totalRows != 6){
17133             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17134             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17135         }
17136         
17137         this.fireEvent('monthchange', this, date);
17138         
17139         
17140         /*
17141         if(!this.internalRender){
17142             var main = this.el.dom.firstChild;
17143             var w = main.offsetWidth;
17144             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17145             Roo.fly(main).setWidth(w);
17146             this.internalRender = true;
17147             // opera does not respect the auto grow header center column
17148             // then, after it gets a width opera refuses to recalculate
17149             // without a second pass
17150             if(Roo.isOpera && !this.secondPass){
17151                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17152                 this.secondPass = true;
17153                 this.update.defer(10, this, [date]);
17154             }
17155         }
17156         */
17157         
17158     },
17159     
17160     findCell : function(dt) {
17161         dt = dt.clearTime().getTime();
17162         var ret = false;
17163         this.cells.each(function(c){
17164             //Roo.log("check " +c.dateValue + '?=' + dt);
17165             if(c.dateValue == dt){
17166                 ret = c;
17167                 return false;
17168             }
17169             return true;
17170         });
17171         
17172         return ret;
17173     },
17174     
17175     findCells : function(ev) {
17176         var s = ev.start.clone().clearTime().getTime();
17177        // Roo.log(s);
17178         var e= ev.end.clone().clearTime().getTime();
17179        // Roo.log(e);
17180         var ret = [];
17181         this.cells.each(function(c){
17182              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17183             
17184             if(c.dateValue > e){
17185                 return ;
17186             }
17187             if(c.dateValue < s){
17188                 return ;
17189             }
17190             ret.push(c);
17191         });
17192         
17193         return ret;    
17194     },
17195     
17196 //    findBestRow: function(cells)
17197 //    {
17198 //        var ret = 0;
17199 //        
17200 //        for (var i =0 ; i < cells.length;i++) {
17201 //            ret  = Math.max(cells[i].rows || 0,ret);
17202 //        }
17203 //        return ret;
17204 //        
17205 //    },
17206     
17207     
17208     addItem : function(ev)
17209     {
17210         // look for vertical location slot in
17211         var cells = this.findCells(ev);
17212         
17213 //        ev.row = this.findBestRow(cells);
17214         
17215         // work out the location.
17216         
17217         var crow = false;
17218         var rows = [];
17219         for(var i =0; i < cells.length; i++) {
17220             
17221             cells[i].row = cells[0].row;
17222             
17223             if(i == 0){
17224                 cells[i].row = cells[i].row + 1;
17225             }
17226             
17227             if (!crow) {
17228                 crow = {
17229                     start : cells[i],
17230                     end :  cells[i]
17231                 };
17232                 continue;
17233             }
17234             if (crow.start.getY() == cells[i].getY()) {
17235                 // on same row.
17236                 crow.end = cells[i];
17237                 continue;
17238             }
17239             // different row.
17240             rows.push(crow);
17241             crow = {
17242                 start: cells[i],
17243                 end : cells[i]
17244             };
17245             
17246         }
17247         
17248         rows.push(crow);
17249         ev.els = [];
17250         ev.rows = rows;
17251         ev.cells = cells;
17252         
17253         cells[0].events.push(ev);
17254         
17255         this.calevents.push(ev);
17256     },
17257     
17258     clearEvents: function() {
17259         
17260         if(!this.calevents){
17261             return;
17262         }
17263         
17264         Roo.each(this.cells.elements, function(c){
17265             c.row = 0;
17266             c.events = [];
17267             c.more = [];
17268         });
17269         
17270         Roo.each(this.calevents, function(e) {
17271             Roo.each(e.els, function(el) {
17272                 el.un('mouseenter' ,this.onEventEnter, this);
17273                 el.un('mouseleave' ,this.onEventLeave, this);
17274                 el.remove();
17275             },this);
17276         },this);
17277         
17278         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17279             e.remove();
17280         });
17281         
17282     },
17283     
17284     renderEvents: function()
17285     {   
17286         var _this = this;
17287         
17288         this.cells.each(function(c) {
17289             
17290             if(c.row < 5){
17291                 return;
17292             }
17293             
17294             var ev = c.events;
17295             
17296             var r = 4;
17297             if(c.row != c.events.length){
17298                 r = 4 - (4 - (c.row - c.events.length));
17299             }
17300             
17301             c.events = ev.slice(0, r);
17302             c.more = ev.slice(r);
17303             
17304             if(c.more.length && c.more.length == 1){
17305                 c.events.push(c.more.pop());
17306             }
17307             
17308             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17309             
17310         });
17311             
17312         this.cells.each(function(c) {
17313             
17314             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17315             
17316             
17317             for (var e = 0; e < c.events.length; e++){
17318                 var ev = c.events[e];
17319                 var rows = ev.rows;
17320                 
17321                 for(var i = 0; i < rows.length; i++) {
17322                 
17323                     // how many rows should it span..
17324
17325                     var  cfg = {
17326                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17327                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17328
17329                         unselectable : "on",
17330                         cn : [
17331                             {
17332                                 cls: 'fc-event-inner',
17333                                 cn : [
17334     //                                {
17335     //                                  tag:'span',
17336     //                                  cls: 'fc-event-time',
17337     //                                  html : cells.length > 1 ? '' : ev.time
17338     //                                },
17339                                     {
17340                                       tag:'span',
17341                                       cls: 'fc-event-title',
17342                                       html : String.format('{0}', ev.title)
17343                                     }
17344
17345
17346                                 ]
17347                             },
17348                             {
17349                                 cls: 'ui-resizable-handle ui-resizable-e',
17350                                 html : '&nbsp;&nbsp;&nbsp'
17351                             }
17352
17353                         ]
17354                     };
17355
17356                     if (i == 0) {
17357                         cfg.cls += ' fc-event-start';
17358                     }
17359                     if ((i+1) == rows.length) {
17360                         cfg.cls += ' fc-event-end';
17361                     }
17362
17363                     var ctr = _this.el.select('.fc-event-container',true).first();
17364                     var cg = ctr.createChild(cfg);
17365
17366                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17367                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17368
17369                     var r = (c.more.length) ? 1 : 0;
17370                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17371                     cg.setWidth(ebox.right - sbox.x -2);
17372
17373                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17374                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17375                     cg.on('click', _this.onEventClick, _this, ev);
17376
17377                     ev.els.push(cg);
17378                     
17379                 }
17380                 
17381             }
17382             
17383             
17384             if(c.more.length){
17385                 var  cfg = {
17386                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17387                     style : 'position: absolute',
17388                     unselectable : "on",
17389                     cn : [
17390                         {
17391                             cls: 'fc-event-inner',
17392                             cn : [
17393                                 {
17394                                   tag:'span',
17395                                   cls: 'fc-event-title',
17396                                   html : 'More'
17397                                 }
17398
17399
17400                             ]
17401                         },
17402                         {
17403                             cls: 'ui-resizable-handle ui-resizable-e',
17404                             html : '&nbsp;&nbsp;&nbsp'
17405                         }
17406
17407                     ]
17408                 };
17409
17410                 var ctr = _this.el.select('.fc-event-container',true).first();
17411                 var cg = ctr.createChild(cfg);
17412
17413                 var sbox = c.select('.fc-day-content',true).first().getBox();
17414                 var ebox = c.select('.fc-day-content',true).first().getBox();
17415                 //Roo.log(cg);
17416                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17417                 cg.setWidth(ebox.right - sbox.x -2);
17418
17419                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17420                 
17421             }
17422             
17423         });
17424         
17425         
17426         
17427     },
17428     
17429     onEventEnter: function (e, el,event,d) {
17430         this.fireEvent('evententer', this, el, event);
17431     },
17432     
17433     onEventLeave: function (e, el,event,d) {
17434         this.fireEvent('eventleave', this, el, event);
17435     },
17436     
17437     onEventClick: function (e, el,event,d) {
17438         this.fireEvent('eventclick', this, el, event);
17439     },
17440     
17441     onMonthChange: function () {
17442         this.store.load();
17443     },
17444     
17445     onMoreEventClick: function(e, el, more)
17446     {
17447         var _this = this;
17448         
17449         this.calpopover.placement = 'right';
17450         this.calpopover.setTitle('More');
17451         
17452         this.calpopover.setContent('');
17453         
17454         var ctr = this.calpopover.el.select('.popover-content', true).first();
17455         
17456         Roo.each(more, function(m){
17457             var cfg = {
17458                 cls : 'fc-event-hori fc-event-draggable',
17459                 html : m.title
17460             };
17461             var cg = ctr.createChild(cfg);
17462             
17463             cg.on('click', _this.onEventClick, _this, m);
17464         });
17465         
17466         this.calpopover.show(el);
17467         
17468         
17469     },
17470     
17471     onLoad: function () 
17472     {   
17473         this.calevents = [];
17474         var cal = this;
17475         
17476         if(this.store.getCount() > 0){
17477             this.store.data.each(function(d){
17478                cal.addItem({
17479                     id : d.data.id,
17480                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17481                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17482                     time : d.data.start_time,
17483                     title : d.data.title,
17484                     description : d.data.description,
17485                     venue : d.data.venue
17486                 });
17487             });
17488         }
17489         
17490         this.renderEvents();
17491         
17492         if(this.calevents.length && this.loadMask){
17493             this.maskEl.hide();
17494         }
17495     },
17496     
17497     onBeforeLoad: function()
17498     {
17499         this.clearEvents();
17500         if(this.loadMask){
17501             this.maskEl.show();
17502         }
17503     }
17504 });
17505
17506  
17507  /*
17508  * - LGPL
17509  *
17510  * element
17511  * 
17512  */
17513
17514 /**
17515  * @class Roo.bootstrap.Popover
17516  * @extends Roo.bootstrap.Component
17517  * Bootstrap Popover class
17518  * @cfg {String} html contents of the popover   (or false to use children..)
17519  * @cfg {String} title of popover (or false to hide)
17520  * @cfg {String} placement how it is placed
17521  * @cfg {String} trigger click || hover (or false to trigger manually)
17522  * @cfg {String} over what (parent or false to trigger manually.)
17523  * @cfg {Number} delay - delay before showing
17524  
17525  * @constructor
17526  * Create a new Popover
17527  * @param {Object} config The config object
17528  */
17529
17530 Roo.bootstrap.Popover = function(config){
17531     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17532     
17533     this.addEvents({
17534         // raw events
17535          /**
17536          * @event show
17537          * After the popover show
17538          * 
17539          * @param {Roo.bootstrap.Popover} this
17540          */
17541         "show" : true,
17542         /**
17543          * @event hide
17544          * After the popover hide
17545          * 
17546          * @param {Roo.bootstrap.Popover} this
17547          */
17548         "hide" : true
17549     });
17550 };
17551
17552 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17553     
17554     title: 'Fill in a title',
17555     html: false,
17556     
17557     placement : 'right',
17558     trigger : 'hover', // hover
17559     
17560     delay : 0,
17561     
17562     over: 'parent',
17563     
17564     can_build_overlaid : false,
17565     
17566     getChildContainer : function()
17567     {
17568         return this.el.select('.popover-content',true).first();
17569     },
17570     
17571     getAutoCreate : function(){
17572          
17573         var cfg = {
17574            cls : 'popover roo-dynamic',
17575            style: 'display:block',
17576            cn : [
17577                 {
17578                     cls : 'arrow'
17579                 },
17580                 {
17581                     cls : 'popover-inner',
17582                     cn : [
17583                         {
17584                             tag: 'h3',
17585                             cls: 'popover-title',
17586                             html : this.title
17587                         },
17588                         {
17589                             cls : 'popover-content',
17590                             html : this.html
17591                         }
17592                     ]
17593                     
17594                 }
17595            ]
17596         };
17597         
17598         return cfg;
17599     },
17600     setTitle: function(str)
17601     {
17602         this.title = str;
17603         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17604     },
17605     setContent: function(str)
17606     {
17607         this.html = str;
17608         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17609     },
17610     // as it get's added to the bottom of the page.
17611     onRender : function(ct, position)
17612     {
17613         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17614         if(!this.el){
17615             var cfg = Roo.apply({},  this.getAutoCreate());
17616             cfg.id = Roo.id();
17617             
17618             if (this.cls) {
17619                 cfg.cls += ' ' + this.cls;
17620             }
17621             if (this.style) {
17622                 cfg.style = this.style;
17623             }
17624             //Roo.log("adding to ");
17625             this.el = Roo.get(document.body).createChild(cfg, position);
17626 //            Roo.log(this.el);
17627         }
17628         this.initEvents();
17629     },
17630     
17631     initEvents : function()
17632     {
17633         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17634         this.el.enableDisplayMode('block');
17635         this.el.hide();
17636         if (this.over === false) {
17637             return; 
17638         }
17639         if (this.triggers === false) {
17640             return;
17641         }
17642         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17643         var triggers = this.trigger ? this.trigger.split(' ') : [];
17644         Roo.each(triggers, function(trigger) {
17645         
17646             if (trigger == 'click') {
17647                 on_el.on('click', this.toggle, this);
17648             } else if (trigger != 'manual') {
17649                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17650                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17651       
17652                 on_el.on(eventIn  ,this.enter, this);
17653                 on_el.on(eventOut, this.leave, this);
17654             }
17655         }, this);
17656         
17657     },
17658     
17659     
17660     // private
17661     timeout : null,
17662     hoverState : null,
17663     
17664     toggle : function () {
17665         this.hoverState == 'in' ? this.leave() : this.enter();
17666     },
17667     
17668     enter : function () {
17669         
17670         clearTimeout(this.timeout);
17671     
17672         this.hoverState = 'in';
17673     
17674         if (!this.delay || !this.delay.show) {
17675             this.show();
17676             return;
17677         }
17678         var _t = this;
17679         this.timeout = setTimeout(function () {
17680             if (_t.hoverState == 'in') {
17681                 _t.show();
17682             }
17683         }, this.delay.show)
17684     },
17685     
17686     leave : function() {
17687         clearTimeout(this.timeout);
17688     
17689         this.hoverState = 'out';
17690     
17691         if (!this.delay || !this.delay.hide) {
17692             this.hide();
17693             return;
17694         }
17695         var _t = this;
17696         this.timeout = setTimeout(function () {
17697             if (_t.hoverState == 'out') {
17698                 _t.hide();
17699             }
17700         }, this.delay.hide)
17701     },
17702     
17703     show : function (on_el)
17704     {
17705         if (!on_el) {
17706             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17707         }
17708         
17709         // set content.
17710         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17711         if (this.html !== false) {
17712             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17713         }
17714         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17715         if (!this.title.length) {
17716             this.el.select('.popover-title',true).hide();
17717         }
17718         
17719         var placement = typeof this.placement == 'function' ?
17720             this.placement.call(this, this.el, on_el) :
17721             this.placement;
17722             
17723         var autoToken = /\s?auto?\s?/i;
17724         var autoPlace = autoToken.test(placement);
17725         if (autoPlace) {
17726             placement = placement.replace(autoToken, '') || 'top';
17727         }
17728         
17729         //this.el.detach()
17730         //this.el.setXY([0,0]);
17731         this.el.show();
17732         this.el.dom.style.display='block';
17733         this.el.addClass(placement);
17734         
17735         //this.el.appendTo(on_el);
17736         
17737         var p = this.getPosition();
17738         var box = this.el.getBox();
17739         
17740         if (autoPlace) {
17741             // fixme..
17742         }
17743         var align = Roo.bootstrap.Popover.alignment[placement];
17744         
17745 //        Roo.log(align);
17746         this.el.alignTo(on_el, align[0],align[1]);
17747         //var arrow = this.el.select('.arrow',true).first();
17748         //arrow.set(align[2], 
17749         
17750         this.el.addClass('in');
17751         
17752         
17753         if (this.el.hasClass('fade')) {
17754             // fade it?
17755         }
17756         
17757         this.hoverState = 'in';
17758         
17759         this.fireEvent('show', this);
17760         
17761     },
17762     hide : function()
17763     {
17764         this.el.setXY([0,0]);
17765         this.el.removeClass('in');
17766         this.el.hide();
17767         this.hoverState = null;
17768         
17769         this.fireEvent('hide', this);
17770     }
17771     
17772 });
17773
17774 Roo.bootstrap.Popover.alignment = {
17775     'left' : ['r-l', [-10,0], 'right'],
17776     'right' : ['l-r', [10,0], 'left'],
17777     'bottom' : ['t-b', [0,10], 'top'],
17778     'top' : [ 'b-t', [0,-10], 'bottom']
17779 };
17780
17781  /*
17782  * - LGPL
17783  *
17784  * Progress
17785  * 
17786  */
17787
17788 /**
17789  * @class Roo.bootstrap.Progress
17790  * @extends Roo.bootstrap.Component
17791  * Bootstrap Progress class
17792  * @cfg {Boolean} striped striped of the progress bar
17793  * @cfg {Boolean} active animated of the progress bar
17794  * 
17795  * 
17796  * @constructor
17797  * Create a new Progress
17798  * @param {Object} config The config object
17799  */
17800
17801 Roo.bootstrap.Progress = function(config){
17802     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17803 };
17804
17805 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17806     
17807     striped : false,
17808     active: false,
17809     
17810     getAutoCreate : function(){
17811         var cfg = {
17812             tag: 'div',
17813             cls: 'progress'
17814         };
17815         
17816         
17817         if(this.striped){
17818             cfg.cls += ' progress-striped';
17819         }
17820       
17821         if(this.active){
17822             cfg.cls += ' active';
17823         }
17824         
17825         
17826         return cfg;
17827     }
17828    
17829 });
17830
17831  
17832
17833  /*
17834  * - LGPL
17835  *
17836  * ProgressBar
17837  * 
17838  */
17839
17840 /**
17841  * @class Roo.bootstrap.ProgressBar
17842  * @extends Roo.bootstrap.Component
17843  * Bootstrap ProgressBar class
17844  * @cfg {Number} aria_valuenow aria-value now
17845  * @cfg {Number} aria_valuemin aria-value min
17846  * @cfg {Number} aria_valuemax aria-value max
17847  * @cfg {String} label label for the progress bar
17848  * @cfg {String} panel (success | info | warning | danger )
17849  * @cfg {String} role role of the progress bar
17850  * @cfg {String} sr_only text
17851  * 
17852  * 
17853  * @constructor
17854  * Create a new ProgressBar
17855  * @param {Object} config The config object
17856  */
17857
17858 Roo.bootstrap.ProgressBar = function(config){
17859     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17860 };
17861
17862 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17863     
17864     aria_valuenow : 0,
17865     aria_valuemin : 0,
17866     aria_valuemax : 100,
17867     label : false,
17868     panel : false,
17869     role : false,
17870     sr_only: false,
17871     
17872     getAutoCreate : function()
17873     {
17874         
17875         var cfg = {
17876             tag: 'div',
17877             cls: 'progress-bar',
17878             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17879         };
17880         
17881         if(this.sr_only){
17882             cfg.cn = {
17883                 tag: 'span',
17884                 cls: 'sr-only',
17885                 html: this.sr_only
17886             }
17887         }
17888         
17889         if(this.role){
17890             cfg.role = this.role;
17891         }
17892         
17893         if(this.aria_valuenow){
17894             cfg['aria-valuenow'] = this.aria_valuenow;
17895         }
17896         
17897         if(this.aria_valuemin){
17898             cfg['aria-valuemin'] = this.aria_valuemin;
17899         }
17900         
17901         if(this.aria_valuemax){
17902             cfg['aria-valuemax'] = this.aria_valuemax;
17903         }
17904         
17905         if(this.label && !this.sr_only){
17906             cfg.html = this.label;
17907         }
17908         
17909         if(this.panel){
17910             cfg.cls += ' progress-bar-' + this.panel;
17911         }
17912         
17913         return cfg;
17914     },
17915     
17916     update : function(aria_valuenow)
17917     {
17918         this.aria_valuenow = aria_valuenow;
17919         
17920         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17921     }
17922    
17923 });
17924
17925  
17926
17927  /*
17928  * - LGPL
17929  *
17930  * column
17931  * 
17932  */
17933
17934 /**
17935  * @class Roo.bootstrap.TabGroup
17936  * @extends Roo.bootstrap.Column
17937  * Bootstrap Column class
17938  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17939  * @cfg {Boolean} carousel true to make the group behave like a carousel
17940  * @cfg {Boolean} bullets show bullets for the panels
17941  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17942  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17943  * @cfg {Boolean} showarrow (true|false) show arrow default true
17944  * 
17945  * @constructor
17946  * Create a new TabGroup
17947  * @param {Object} config The config object
17948  */
17949
17950 Roo.bootstrap.TabGroup = function(config){
17951     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17952     if (!this.navId) {
17953         this.navId = Roo.id();
17954     }
17955     this.tabs = [];
17956     Roo.bootstrap.TabGroup.register(this);
17957     
17958 };
17959
17960 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17961     
17962     carousel : false,
17963     transition : false,
17964     bullets : 0,
17965     timer : 0,
17966     autoslide : false,
17967     slideFn : false,
17968     slideOnTouch : false,
17969     showarrow : true,
17970     
17971     getAutoCreate : function()
17972     {
17973         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17974         
17975         cfg.cls += ' tab-content';
17976         
17977         if (this.carousel) {
17978             cfg.cls += ' carousel slide';
17979             
17980             cfg.cn = [{
17981                cls : 'carousel-inner',
17982                cn : []
17983             }];
17984         
17985             if(this.bullets  && !Roo.isTouch){
17986                 
17987                 var bullets = {
17988                     cls : 'carousel-bullets',
17989                     cn : []
17990                 };
17991                
17992                 if(this.bullets_cls){
17993                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17994                 }
17995                 
17996                 bullets.cn.push({
17997                     cls : 'clear'
17998                 });
17999                 
18000                 cfg.cn[0].cn.push(bullets);
18001             }
18002             
18003             if(this.showarrow){
18004                 cfg.cn[0].cn.push({
18005                     tag : 'div',
18006                     class : 'carousel-arrow',
18007                     cn : [
18008                         {
18009                             tag : 'div',
18010                             class : 'carousel-prev',
18011                             cn : [
18012                                 {
18013                                     tag : 'i',
18014                                     class : 'fa fa-chevron-left'
18015                                 }
18016                             ]
18017                         },
18018                         {
18019                             tag : 'div',
18020                             class : 'carousel-next',
18021                             cn : [
18022                                 {
18023                                     tag : 'i',
18024                                     class : 'fa fa-chevron-right'
18025                                 }
18026                             ]
18027                         }
18028                     ]
18029                 });
18030             }
18031             
18032         }
18033         
18034         return cfg;
18035     },
18036     
18037     initEvents:  function()
18038     {
18039 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18040 //            this.el.on("touchstart", this.onTouchStart, this);
18041 //        }
18042         
18043         if(this.autoslide){
18044             var _this = this;
18045             
18046             this.slideFn = window.setInterval(function() {
18047                 _this.showPanelNext();
18048             }, this.timer);
18049         }
18050         
18051         if(this.showarrow){
18052             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18053             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18054         }
18055         
18056         
18057     },
18058     
18059 //    onTouchStart : function(e, el, o)
18060 //    {
18061 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18062 //            return;
18063 //        }
18064 //        
18065 //        this.showPanelNext();
18066 //    },
18067     
18068     
18069     getChildContainer : function()
18070     {
18071         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18072     },
18073     
18074     /**
18075     * register a Navigation item
18076     * @param {Roo.bootstrap.NavItem} the navitem to add
18077     */
18078     register : function(item)
18079     {
18080         this.tabs.push( item);
18081         item.navId = this.navId; // not really needed..
18082         this.addBullet();
18083     
18084     },
18085     
18086     getActivePanel : function()
18087     {
18088         var r = false;
18089         Roo.each(this.tabs, function(t) {
18090             if (t.active) {
18091                 r = t;
18092                 return false;
18093             }
18094             return null;
18095         });
18096         return r;
18097         
18098     },
18099     getPanelByName : function(n)
18100     {
18101         var r = false;
18102         Roo.each(this.tabs, function(t) {
18103             if (t.tabId == n) {
18104                 r = t;
18105                 return false;
18106             }
18107             return null;
18108         });
18109         return r;
18110     },
18111     indexOfPanel : function(p)
18112     {
18113         var r = false;
18114         Roo.each(this.tabs, function(t,i) {
18115             if (t.tabId == p.tabId) {
18116                 r = i;
18117                 return false;
18118             }
18119             return null;
18120         });
18121         return r;
18122     },
18123     /**
18124      * show a specific panel
18125      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18126      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18127      */
18128     showPanel : function (pan)
18129     {
18130         if(this.transition || typeof(pan) == 'undefined'){
18131             Roo.log("waiting for the transitionend");
18132             return;
18133         }
18134         
18135         if (typeof(pan) == 'number') {
18136             pan = this.tabs[pan];
18137         }
18138         
18139         if (typeof(pan) == 'string') {
18140             pan = this.getPanelByName(pan);
18141         }
18142         
18143         var cur = this.getActivePanel();
18144         
18145         if(!pan || !cur){
18146             Roo.log('pan or acitve pan is undefined');
18147             return false;
18148         }
18149         
18150         if (pan.tabId == this.getActivePanel().tabId) {
18151             return true;
18152         }
18153         
18154         if (false === cur.fireEvent('beforedeactivate')) {
18155             return false;
18156         }
18157         
18158         if(this.bullets > 0 && !Roo.isTouch){
18159             this.setActiveBullet(this.indexOfPanel(pan));
18160         }
18161         
18162         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18163             
18164             this.transition = true;
18165             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18166             var lr = dir == 'next' ? 'left' : 'right';
18167             pan.el.addClass(dir); // or prev
18168             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18169             cur.el.addClass(lr); // or right
18170             pan.el.addClass(lr);
18171             
18172             var _this = this;
18173             cur.el.on('transitionend', function() {
18174                 Roo.log("trans end?");
18175                 
18176                 pan.el.removeClass([lr,dir]);
18177                 pan.setActive(true);
18178                 
18179                 cur.el.removeClass([lr]);
18180                 cur.setActive(false);
18181                 
18182                 _this.transition = false;
18183                 
18184             }, this, { single:  true } );
18185             
18186             return true;
18187         }
18188         
18189         cur.setActive(false);
18190         pan.setActive(true);
18191         
18192         return true;
18193         
18194     },
18195     showPanelNext : function()
18196     {
18197         var i = this.indexOfPanel(this.getActivePanel());
18198         
18199         if (i >= this.tabs.length - 1 && !this.autoslide) {
18200             return;
18201         }
18202         
18203         if (i >= this.tabs.length - 1 && this.autoslide) {
18204             i = -1;
18205         }
18206         
18207         this.showPanel(this.tabs[i+1]);
18208     },
18209     
18210     showPanelPrev : function()
18211     {
18212         var i = this.indexOfPanel(this.getActivePanel());
18213         
18214         if (i  < 1 && !this.autoslide) {
18215             return;
18216         }
18217         
18218         if (i < 1 && this.autoslide) {
18219             i = this.tabs.length;
18220         }
18221         
18222         this.showPanel(this.tabs[i-1]);
18223     },
18224     
18225     
18226     addBullet: function()
18227     {
18228         if(!this.bullets || Roo.isTouch){
18229             return;
18230         }
18231         var ctr = this.el.select('.carousel-bullets',true).first();
18232         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18233         var bullet = ctr.createChild({
18234             cls : 'bullet bullet-' + i
18235         },ctr.dom.lastChild);
18236         
18237         
18238         var _this = this;
18239         
18240         bullet.on('click', (function(e, el, o, ii, t){
18241
18242             e.preventDefault();
18243
18244             this.showPanel(ii);
18245
18246             if(this.autoslide && this.slideFn){
18247                 clearInterval(this.slideFn);
18248                 this.slideFn = window.setInterval(function() {
18249                     _this.showPanelNext();
18250                 }, this.timer);
18251             }
18252
18253         }).createDelegate(this, [i, bullet], true));
18254                 
18255         
18256     },
18257      
18258     setActiveBullet : function(i)
18259     {
18260         if(Roo.isTouch){
18261             return;
18262         }
18263         
18264         Roo.each(this.el.select('.bullet', true).elements, function(el){
18265             el.removeClass('selected');
18266         });
18267
18268         var bullet = this.el.select('.bullet-' + i, true).first();
18269         
18270         if(!bullet){
18271             return;
18272         }
18273         
18274         bullet.addClass('selected');
18275     }
18276     
18277     
18278   
18279 });
18280
18281  
18282
18283  
18284  
18285 Roo.apply(Roo.bootstrap.TabGroup, {
18286     
18287     groups: {},
18288      /**
18289     * register a Navigation Group
18290     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18291     */
18292     register : function(navgrp)
18293     {
18294         this.groups[navgrp.navId] = navgrp;
18295         
18296     },
18297     /**
18298     * fetch a Navigation Group based on the navigation ID
18299     * if one does not exist , it will get created.
18300     * @param {string} the navgroup to add
18301     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18302     */
18303     get: function(navId) {
18304         if (typeof(this.groups[navId]) == 'undefined') {
18305             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18306         }
18307         return this.groups[navId] ;
18308     }
18309     
18310     
18311     
18312 });
18313
18314  /*
18315  * - LGPL
18316  *
18317  * TabPanel
18318  * 
18319  */
18320
18321 /**
18322  * @class Roo.bootstrap.TabPanel
18323  * @extends Roo.bootstrap.Component
18324  * Bootstrap TabPanel class
18325  * @cfg {Boolean} active panel active
18326  * @cfg {String} html panel content
18327  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18328  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18329  * @cfg {String} href click to link..
18330  * 
18331  * 
18332  * @constructor
18333  * Create a new TabPanel
18334  * @param {Object} config The config object
18335  */
18336
18337 Roo.bootstrap.TabPanel = function(config){
18338     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18339     this.addEvents({
18340         /**
18341              * @event changed
18342              * Fires when the active status changes
18343              * @param {Roo.bootstrap.TabPanel} this
18344              * @param {Boolean} state the new state
18345             
18346          */
18347         'changed': true,
18348         /**
18349              * @event beforedeactivate
18350              * Fires before a tab is de-activated - can be used to do validation on a form.
18351              * @param {Roo.bootstrap.TabPanel} this
18352              * @return {Boolean} false if there is an error
18353             
18354          */
18355         'beforedeactivate': true
18356      });
18357     
18358     this.tabId = this.tabId || Roo.id();
18359   
18360 };
18361
18362 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18363     
18364     active: false,
18365     html: false,
18366     tabId: false,
18367     navId : false,
18368     href : '',
18369     
18370     getAutoCreate : function(){
18371         var cfg = {
18372             tag: 'div',
18373             // item is needed for carousel - not sure if it has any effect otherwise
18374             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18375             html: this.html || ''
18376         };
18377         
18378         if(this.active){
18379             cfg.cls += ' active';
18380         }
18381         
18382         if(this.tabId){
18383             cfg.tabId = this.tabId;
18384         }
18385         
18386         
18387         return cfg;
18388     },
18389     
18390     initEvents:  function()
18391     {
18392         var p = this.parent();
18393         
18394         this.navId = this.navId || p.navId;
18395         
18396         if (typeof(this.navId) != 'undefined') {
18397             // not really needed.. but just in case.. parent should be a NavGroup.
18398             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18399             
18400             tg.register(this);
18401             
18402             var i = tg.tabs.length - 1;
18403             
18404             if(this.active && tg.bullets > 0 && i < tg.bullets){
18405                 tg.setActiveBullet(i);
18406             }
18407         }
18408         
18409         this.el.on('click', this.onClick, this);
18410         
18411         if(Roo.isTouch){
18412             this.el.on("touchstart", this.onTouchStart, this);
18413             this.el.on("touchmove", this.onTouchMove, this);
18414             this.el.on("touchend", this.onTouchEnd, this);
18415         }
18416         
18417     },
18418     
18419     onRender : function(ct, position)
18420     {
18421         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18422     },
18423     
18424     setActive : function(state)
18425     {
18426         Roo.log("panel - set active " + this.tabId + "=" + state);
18427         
18428         this.active = state;
18429         if (!state) {
18430             this.el.removeClass('active');
18431             
18432         } else  if (!this.el.hasClass('active')) {
18433             this.el.addClass('active');
18434         }
18435         
18436         this.fireEvent('changed', this, state);
18437     },
18438     
18439     onClick : function(e)
18440     {
18441         e.preventDefault();
18442         
18443         if(!this.href.length){
18444             return;
18445         }
18446         
18447         window.location.href = this.href;
18448     },
18449     
18450     startX : 0,
18451     startY : 0,
18452     endX : 0,
18453     endY : 0,
18454     swiping : false,
18455     
18456     onTouchStart : function(e)
18457     {
18458         this.swiping = false;
18459         
18460         this.startX = e.browserEvent.touches[0].clientX;
18461         this.startY = e.browserEvent.touches[0].clientY;
18462     },
18463     
18464     onTouchMove : function(e)
18465     {
18466         this.swiping = true;
18467         
18468         this.endX = e.browserEvent.touches[0].clientX;
18469         this.endY = e.browserEvent.touches[0].clientY;
18470     },
18471     
18472     onTouchEnd : function(e)
18473     {
18474         if(!this.swiping){
18475             this.onClick(e);
18476             return;
18477         }
18478         
18479         var tabGroup = this.parent();
18480         
18481         if(this.endX > this.startX){ // swiping right
18482             tabGroup.showPanelPrev();
18483             return;
18484         }
18485         
18486         if(this.startX > this.endX){ // swiping left
18487             tabGroup.showPanelNext();
18488             return;
18489         }
18490     }
18491     
18492     
18493 });
18494  
18495
18496  
18497
18498  /*
18499  * - LGPL
18500  *
18501  * DateField
18502  * 
18503  */
18504
18505 /**
18506  * @class Roo.bootstrap.DateField
18507  * @extends Roo.bootstrap.Input
18508  * Bootstrap DateField class
18509  * @cfg {Number} weekStart default 0
18510  * @cfg {String} viewMode default empty, (months|years)
18511  * @cfg {String} minViewMode default empty, (months|years)
18512  * @cfg {Number} startDate default -Infinity
18513  * @cfg {Number} endDate default Infinity
18514  * @cfg {Boolean} todayHighlight default false
18515  * @cfg {Boolean} todayBtn default false
18516  * @cfg {Boolean} calendarWeeks default false
18517  * @cfg {Object} daysOfWeekDisabled default empty
18518  * @cfg {Boolean} singleMode default false (true | false)
18519  * 
18520  * @cfg {Boolean} keyboardNavigation default true
18521  * @cfg {String} language default en
18522  * 
18523  * @constructor
18524  * Create a new DateField
18525  * @param {Object} config The config object
18526  */
18527
18528 Roo.bootstrap.DateField = function(config){
18529     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18530      this.addEvents({
18531             /**
18532              * @event show
18533              * Fires when this field show.
18534              * @param {Roo.bootstrap.DateField} this
18535              * @param {Mixed} date The date value
18536              */
18537             show : true,
18538             /**
18539              * @event show
18540              * Fires when this field hide.
18541              * @param {Roo.bootstrap.DateField} this
18542              * @param {Mixed} date The date value
18543              */
18544             hide : true,
18545             /**
18546              * @event select
18547              * Fires when select a date.
18548              * @param {Roo.bootstrap.DateField} this
18549              * @param {Mixed} date The date value
18550              */
18551             select : true,
18552             /**
18553              * @event beforeselect
18554              * Fires when before select a date.
18555              * @param {Roo.bootstrap.DateField} this
18556              * @param {Mixed} date The date value
18557              */
18558             beforeselect : true
18559         });
18560 };
18561
18562 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18563     
18564     /**
18565      * @cfg {String} format
18566      * The default date format string which can be overriden for localization support.  The format must be
18567      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18568      */
18569     format : "m/d/y",
18570     /**
18571      * @cfg {String} altFormats
18572      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18573      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18574      */
18575     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18576     
18577     weekStart : 0,
18578     
18579     viewMode : '',
18580     
18581     minViewMode : '',
18582     
18583     todayHighlight : false,
18584     
18585     todayBtn: false,
18586     
18587     language: 'en',
18588     
18589     keyboardNavigation: true,
18590     
18591     calendarWeeks: false,
18592     
18593     startDate: -Infinity,
18594     
18595     endDate: Infinity,
18596     
18597     daysOfWeekDisabled: [],
18598     
18599     _events: [],
18600     
18601     singleMode : false,
18602     
18603     UTCDate: function()
18604     {
18605         return new Date(Date.UTC.apply(Date, arguments));
18606     },
18607     
18608     UTCToday: function()
18609     {
18610         var today = new Date();
18611         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18612     },
18613     
18614     getDate: function() {
18615             var d = this.getUTCDate();
18616             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18617     },
18618     
18619     getUTCDate: function() {
18620             return this.date;
18621     },
18622     
18623     setDate: function(d) {
18624             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18625     },
18626     
18627     setUTCDate: function(d) {
18628             this.date = d;
18629             this.setValue(this.formatDate(this.date));
18630     },
18631         
18632     onRender: function(ct, position)
18633     {
18634         
18635         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18636         
18637         this.language = this.language || 'en';
18638         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18639         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18640         
18641         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18642         this.format = this.format || 'm/d/y';
18643         this.isInline = false;
18644         this.isInput = true;
18645         this.component = this.el.select('.add-on', true).first() || false;
18646         this.component = (this.component && this.component.length === 0) ? false : this.component;
18647         this.hasInput = this.component && this.inputEl().length;
18648         
18649         if (typeof(this.minViewMode === 'string')) {
18650             switch (this.minViewMode) {
18651                 case 'months':
18652                     this.minViewMode = 1;
18653                     break;
18654                 case 'years':
18655                     this.minViewMode = 2;
18656                     break;
18657                 default:
18658                     this.minViewMode = 0;
18659                     break;
18660             }
18661         }
18662         
18663         if (typeof(this.viewMode === 'string')) {
18664             switch (this.viewMode) {
18665                 case 'months':
18666                     this.viewMode = 1;
18667                     break;
18668                 case 'years':
18669                     this.viewMode = 2;
18670                     break;
18671                 default:
18672                     this.viewMode = 0;
18673                     break;
18674             }
18675         }
18676                 
18677         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18678         
18679 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18680         
18681         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18682         
18683         this.picker().on('mousedown', this.onMousedown, this);
18684         this.picker().on('click', this.onClick, this);
18685         
18686         this.picker().addClass('datepicker-dropdown');
18687         
18688         this.startViewMode = this.viewMode;
18689         
18690         if(this.singleMode){
18691             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18692                 v.setVisibilityMode(Roo.Element.DISPLAY);
18693                 v.hide();
18694             });
18695             
18696             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18697                 v.setStyle('width', '189px');
18698             });
18699         }
18700         
18701         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18702             if(!this.calendarWeeks){
18703                 v.remove();
18704                 return;
18705             }
18706             
18707             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18708             v.attr('colspan', function(i, val){
18709                 return parseInt(val) + 1;
18710             });
18711         });
18712                         
18713         
18714         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18715         
18716         this.setStartDate(this.startDate);
18717         this.setEndDate(this.endDate);
18718         
18719         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18720         
18721         this.fillDow();
18722         this.fillMonths();
18723         this.update();
18724         this.showMode();
18725         
18726         if(this.isInline) {
18727             this.showPopup();
18728         }
18729     },
18730     
18731     picker : function()
18732     {
18733         return this.pickerEl;
18734 //        return this.el.select('.datepicker', true).first();
18735     },
18736     
18737     fillDow: function()
18738     {
18739         var dowCnt = this.weekStart;
18740         
18741         var dow = {
18742             tag: 'tr',
18743             cn: [
18744                 
18745             ]
18746         };
18747         
18748         if(this.calendarWeeks){
18749             dow.cn.push({
18750                 tag: 'th',
18751                 cls: 'cw',
18752                 html: '&nbsp;'
18753             })
18754         }
18755         
18756         while (dowCnt < this.weekStart + 7) {
18757             dow.cn.push({
18758                 tag: 'th',
18759                 cls: 'dow',
18760                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18761             });
18762         }
18763         
18764         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18765     },
18766     
18767     fillMonths: function()
18768     {    
18769         var i = 0;
18770         var months = this.picker().select('>.datepicker-months td', true).first();
18771         
18772         months.dom.innerHTML = '';
18773         
18774         while (i < 12) {
18775             var month = {
18776                 tag: 'span',
18777                 cls: 'month',
18778                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18779             };
18780             
18781             months.createChild(month);
18782         }
18783         
18784     },
18785     
18786     update: function()
18787     {
18788         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;
18789         
18790         if (this.date < this.startDate) {
18791             this.viewDate = new Date(this.startDate);
18792         } else if (this.date > this.endDate) {
18793             this.viewDate = new Date(this.endDate);
18794         } else {
18795             this.viewDate = new Date(this.date);
18796         }
18797         
18798         this.fill();
18799     },
18800     
18801     fill: function() 
18802     {
18803         var d = new Date(this.viewDate),
18804                 year = d.getUTCFullYear(),
18805                 month = d.getUTCMonth(),
18806                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18807                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18808                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18809                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18810                 currentDate = this.date && this.date.valueOf(),
18811                 today = this.UTCToday();
18812         
18813         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18814         
18815 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18816         
18817 //        this.picker.select('>tfoot th.today').
18818 //                                              .text(dates[this.language].today)
18819 //                                              .toggle(this.todayBtn !== false);
18820     
18821         this.updateNavArrows();
18822         this.fillMonths();
18823                                                 
18824         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18825         
18826         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18827          
18828         prevMonth.setUTCDate(day);
18829         
18830         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18831         
18832         var nextMonth = new Date(prevMonth);
18833         
18834         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18835         
18836         nextMonth = nextMonth.valueOf();
18837         
18838         var fillMonths = false;
18839         
18840         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18841         
18842         while(prevMonth.valueOf() <= nextMonth) {
18843             var clsName = '';
18844             
18845             if (prevMonth.getUTCDay() === this.weekStart) {
18846                 if(fillMonths){
18847                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18848                 }
18849                     
18850                 fillMonths = {
18851                     tag: 'tr',
18852                     cn: []
18853                 };
18854                 
18855                 if(this.calendarWeeks){
18856                     // ISO 8601: First week contains first thursday.
18857                     // ISO also states week starts on Monday, but we can be more abstract here.
18858                     var
18859                     // Start of current week: based on weekstart/current date
18860                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18861                     // Thursday of this week
18862                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18863                     // First Thursday of year, year from thursday
18864                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18865                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18866                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18867                     
18868                     fillMonths.cn.push({
18869                         tag: 'td',
18870                         cls: 'cw',
18871                         html: calWeek
18872                     });
18873                 }
18874             }
18875             
18876             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18877                 clsName += ' old';
18878             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18879                 clsName += ' new';
18880             }
18881             if (this.todayHighlight &&
18882                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18883                 prevMonth.getUTCMonth() == today.getMonth() &&
18884                 prevMonth.getUTCDate() == today.getDate()) {
18885                 clsName += ' today';
18886             }
18887             
18888             if (currentDate && prevMonth.valueOf() === currentDate) {
18889                 clsName += ' active';
18890             }
18891             
18892             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18893                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18894                     clsName += ' disabled';
18895             }
18896             
18897             fillMonths.cn.push({
18898                 tag: 'td',
18899                 cls: 'day ' + clsName,
18900                 html: prevMonth.getDate()
18901             });
18902             
18903             prevMonth.setDate(prevMonth.getDate()+1);
18904         }
18905           
18906         var currentYear = this.date && this.date.getUTCFullYear();
18907         var currentMonth = this.date && this.date.getUTCMonth();
18908         
18909         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18910         
18911         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18912             v.removeClass('active');
18913             
18914             if(currentYear === year && k === currentMonth){
18915                 v.addClass('active');
18916             }
18917             
18918             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18919                 v.addClass('disabled');
18920             }
18921             
18922         });
18923         
18924         
18925         year = parseInt(year/10, 10) * 10;
18926         
18927         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18928         
18929         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18930         
18931         year -= 1;
18932         for (var i = -1; i < 11; i++) {
18933             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18934                 tag: 'span',
18935                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18936                 html: year
18937             });
18938             
18939             year += 1;
18940         }
18941     },
18942     
18943     showMode: function(dir) 
18944     {
18945         if (dir) {
18946             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18947         }
18948         
18949         Roo.each(this.picker().select('>div',true).elements, function(v){
18950             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18951             v.hide();
18952         });
18953         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18954     },
18955     
18956     place: function()
18957     {
18958         if(this.isInline) {
18959             return;
18960         }
18961         
18962         this.picker().removeClass(['bottom', 'top']);
18963         
18964         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18965             /*
18966              * place to the top of element!
18967              *
18968              */
18969             
18970             this.picker().addClass('top');
18971             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18972             
18973             return;
18974         }
18975         
18976         this.picker().addClass('bottom');
18977         
18978         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18979     },
18980     
18981     parseDate : function(value)
18982     {
18983         if(!value || value instanceof Date){
18984             return value;
18985         }
18986         var v = Date.parseDate(value, this.format);
18987         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18988             v = Date.parseDate(value, 'Y-m-d');
18989         }
18990         if(!v && this.altFormats){
18991             if(!this.altFormatsArray){
18992                 this.altFormatsArray = this.altFormats.split("|");
18993             }
18994             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18995                 v = Date.parseDate(value, this.altFormatsArray[i]);
18996             }
18997         }
18998         return v;
18999     },
19000     
19001     formatDate : function(date, fmt)
19002     {   
19003         return (!date || !(date instanceof Date)) ?
19004         date : date.dateFormat(fmt || this.format);
19005     },
19006     
19007     onFocus : function()
19008     {
19009         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19010         this.showPopup();
19011     },
19012     
19013     onBlur : function()
19014     {
19015         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19016         
19017         var d = this.inputEl().getValue();
19018         
19019         this.setValue(d);
19020                 
19021         this.hidePopup();
19022     },
19023     
19024     showPopup : function()
19025     {
19026         this.picker().show();
19027         this.update();
19028         this.place();
19029         
19030         this.fireEvent('showpopup', this, this.date);
19031     },
19032     
19033     hidePopup : function()
19034     {
19035         if(this.isInline) {
19036             return;
19037         }
19038         this.picker().hide();
19039         this.viewMode = this.startViewMode;
19040         this.showMode();
19041         
19042         this.fireEvent('hidepopup', this, this.date);
19043         
19044     },
19045     
19046     onMousedown: function(e)
19047     {
19048         e.stopPropagation();
19049         e.preventDefault();
19050     },
19051     
19052     keyup: function(e)
19053     {
19054         Roo.bootstrap.DateField.superclass.keyup.call(this);
19055         this.update();
19056     },
19057
19058     setValue: function(v)
19059     {
19060         if(this.fireEvent('beforeselect', this, v) !== false){
19061             var d = new Date(this.parseDate(v) ).clearTime();
19062         
19063             if(isNaN(d.getTime())){
19064                 this.date = this.viewDate = '';
19065                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19066                 return;
19067             }
19068
19069             v = this.formatDate(d);
19070
19071             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19072
19073             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19074
19075             this.update();
19076
19077             this.fireEvent('select', this, this.date);
19078         }
19079     },
19080     
19081     getValue: function()
19082     {
19083         return this.formatDate(this.date);
19084     },
19085     
19086     fireKey: function(e)
19087     {
19088         if (!this.picker().isVisible()){
19089             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19090                 this.showPopup();
19091             }
19092             return;
19093         }
19094         
19095         var dateChanged = false,
19096         dir, day, month,
19097         newDate, newViewDate;
19098         
19099         switch(e.keyCode){
19100             case 27: // escape
19101                 this.hidePopup();
19102                 e.preventDefault();
19103                 break;
19104             case 37: // left
19105             case 39: // right
19106                 if (!this.keyboardNavigation) {
19107                     break;
19108                 }
19109                 dir = e.keyCode == 37 ? -1 : 1;
19110                 
19111                 if (e.ctrlKey){
19112                     newDate = this.moveYear(this.date, dir);
19113                     newViewDate = this.moveYear(this.viewDate, dir);
19114                 } else if (e.shiftKey){
19115                     newDate = this.moveMonth(this.date, dir);
19116                     newViewDate = this.moveMonth(this.viewDate, dir);
19117                 } else {
19118                     newDate = new Date(this.date);
19119                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19120                     newViewDate = new Date(this.viewDate);
19121                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19122                 }
19123                 if (this.dateWithinRange(newDate)){
19124                     this.date = newDate;
19125                     this.viewDate = newViewDate;
19126                     this.setValue(this.formatDate(this.date));
19127 //                    this.update();
19128                     e.preventDefault();
19129                     dateChanged = true;
19130                 }
19131                 break;
19132             case 38: // up
19133             case 40: // down
19134                 if (!this.keyboardNavigation) {
19135                     break;
19136                 }
19137                 dir = e.keyCode == 38 ? -1 : 1;
19138                 if (e.ctrlKey){
19139                     newDate = this.moveYear(this.date, dir);
19140                     newViewDate = this.moveYear(this.viewDate, dir);
19141                 } else if (e.shiftKey){
19142                     newDate = this.moveMonth(this.date, dir);
19143                     newViewDate = this.moveMonth(this.viewDate, dir);
19144                 } else {
19145                     newDate = new Date(this.date);
19146                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19147                     newViewDate = new Date(this.viewDate);
19148                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19149                 }
19150                 if (this.dateWithinRange(newDate)){
19151                     this.date = newDate;
19152                     this.viewDate = newViewDate;
19153                     this.setValue(this.formatDate(this.date));
19154 //                    this.update();
19155                     e.preventDefault();
19156                     dateChanged = true;
19157                 }
19158                 break;
19159             case 13: // enter
19160                 this.setValue(this.formatDate(this.date));
19161                 this.hidePopup();
19162                 e.preventDefault();
19163                 break;
19164             case 9: // tab
19165                 this.setValue(this.formatDate(this.date));
19166                 this.hidePopup();
19167                 break;
19168             case 16: // shift
19169             case 17: // ctrl
19170             case 18: // alt
19171                 break;
19172             default :
19173                 this.hidePopup();
19174                 
19175         }
19176     },
19177     
19178     
19179     onClick: function(e) 
19180     {
19181         e.stopPropagation();
19182         e.preventDefault();
19183         
19184         var target = e.getTarget();
19185         
19186         if(target.nodeName.toLowerCase() === 'i'){
19187             target = Roo.get(target).dom.parentNode;
19188         }
19189         
19190         var nodeName = target.nodeName;
19191         var className = target.className;
19192         var html = target.innerHTML;
19193         //Roo.log(nodeName);
19194         
19195         switch(nodeName.toLowerCase()) {
19196             case 'th':
19197                 switch(className) {
19198                     case 'switch':
19199                         this.showMode(1);
19200                         break;
19201                     case 'prev':
19202                     case 'next':
19203                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19204                         switch(this.viewMode){
19205                                 case 0:
19206                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19207                                         break;
19208                                 case 1:
19209                                 case 2:
19210                                         this.viewDate = this.moveYear(this.viewDate, dir);
19211                                         break;
19212                         }
19213                         this.fill();
19214                         break;
19215                     case 'today':
19216                         var date = new Date();
19217                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19218 //                        this.fill()
19219                         this.setValue(this.formatDate(this.date));
19220                         
19221                         this.hidePopup();
19222                         break;
19223                 }
19224                 break;
19225             case 'span':
19226                 if (className.indexOf('disabled') < 0) {
19227                     this.viewDate.setUTCDate(1);
19228                     if (className.indexOf('month') > -1) {
19229                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19230                     } else {
19231                         var year = parseInt(html, 10) || 0;
19232                         this.viewDate.setUTCFullYear(year);
19233                         
19234                     }
19235                     
19236                     if(this.singleMode){
19237                         this.setValue(this.formatDate(this.viewDate));
19238                         this.hidePopup();
19239                         return;
19240                     }
19241                     
19242                     this.showMode(-1);
19243                     this.fill();
19244                 }
19245                 break;
19246                 
19247             case 'td':
19248                 //Roo.log(className);
19249                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19250                     var day = parseInt(html, 10) || 1;
19251                     var year = this.viewDate.getUTCFullYear(),
19252                         month = this.viewDate.getUTCMonth();
19253
19254                     if (className.indexOf('old') > -1) {
19255                         if(month === 0 ){
19256                             month = 11;
19257                             year -= 1;
19258                         }else{
19259                             month -= 1;
19260                         }
19261                     } else if (className.indexOf('new') > -1) {
19262                         if (month == 11) {
19263                             month = 0;
19264                             year += 1;
19265                         } else {
19266                             month += 1;
19267                         }
19268                     }
19269                     //Roo.log([year,month,day]);
19270                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19271                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19272 //                    this.fill();
19273                     //Roo.log(this.formatDate(this.date));
19274                     this.setValue(this.formatDate(this.date));
19275                     this.hidePopup();
19276                 }
19277                 break;
19278         }
19279     },
19280     
19281     setStartDate: function(startDate)
19282     {
19283         this.startDate = startDate || -Infinity;
19284         if (this.startDate !== -Infinity) {
19285             this.startDate = this.parseDate(this.startDate);
19286         }
19287         this.update();
19288         this.updateNavArrows();
19289     },
19290
19291     setEndDate: function(endDate)
19292     {
19293         this.endDate = endDate || Infinity;
19294         if (this.endDate !== Infinity) {
19295             this.endDate = this.parseDate(this.endDate);
19296         }
19297         this.update();
19298         this.updateNavArrows();
19299     },
19300     
19301     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19302     {
19303         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19304         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19305             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19306         }
19307         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19308             return parseInt(d, 10);
19309         });
19310         this.update();
19311         this.updateNavArrows();
19312     },
19313     
19314     updateNavArrows: function() 
19315     {
19316         if(this.singleMode){
19317             return;
19318         }
19319         
19320         var d = new Date(this.viewDate),
19321         year = d.getUTCFullYear(),
19322         month = d.getUTCMonth();
19323         
19324         Roo.each(this.picker().select('.prev', true).elements, function(v){
19325             v.show();
19326             switch (this.viewMode) {
19327                 case 0:
19328
19329                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19330                         v.hide();
19331                     }
19332                     break;
19333                 case 1:
19334                 case 2:
19335                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19336                         v.hide();
19337                     }
19338                     break;
19339             }
19340         });
19341         
19342         Roo.each(this.picker().select('.next', true).elements, function(v){
19343             v.show();
19344             switch (this.viewMode) {
19345                 case 0:
19346
19347                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19348                         v.hide();
19349                     }
19350                     break;
19351                 case 1:
19352                 case 2:
19353                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19354                         v.hide();
19355                     }
19356                     break;
19357             }
19358         })
19359     },
19360     
19361     moveMonth: function(date, dir)
19362     {
19363         if (!dir) {
19364             return date;
19365         }
19366         var new_date = new Date(date.valueOf()),
19367         day = new_date.getUTCDate(),
19368         month = new_date.getUTCMonth(),
19369         mag = Math.abs(dir),
19370         new_month, test;
19371         dir = dir > 0 ? 1 : -1;
19372         if (mag == 1){
19373             test = dir == -1
19374             // If going back one month, make sure month is not current month
19375             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19376             ? function(){
19377                 return new_date.getUTCMonth() == month;
19378             }
19379             // If going forward one month, make sure month is as expected
19380             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19381             : function(){
19382                 return new_date.getUTCMonth() != new_month;
19383             };
19384             new_month = month + dir;
19385             new_date.setUTCMonth(new_month);
19386             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19387             if (new_month < 0 || new_month > 11) {
19388                 new_month = (new_month + 12) % 12;
19389             }
19390         } else {
19391             // For magnitudes >1, move one month at a time...
19392             for (var i=0; i<mag; i++) {
19393                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19394                 new_date = this.moveMonth(new_date, dir);
19395             }
19396             // ...then reset the day, keeping it in the new month
19397             new_month = new_date.getUTCMonth();
19398             new_date.setUTCDate(day);
19399             test = function(){
19400                 return new_month != new_date.getUTCMonth();
19401             };
19402         }
19403         // Common date-resetting loop -- if date is beyond end of month, make it
19404         // end of month
19405         while (test()){
19406             new_date.setUTCDate(--day);
19407             new_date.setUTCMonth(new_month);
19408         }
19409         return new_date;
19410     },
19411
19412     moveYear: function(date, dir)
19413     {
19414         return this.moveMonth(date, dir*12);
19415     },
19416
19417     dateWithinRange: function(date)
19418     {
19419         return date >= this.startDate && date <= this.endDate;
19420     },
19421
19422     
19423     remove: function() 
19424     {
19425         this.picker().remove();
19426     },
19427     
19428     validateValue : function(value)
19429     {
19430         if(this.getVisibilityEl().hasClass('hidden')){
19431             return true;
19432         }
19433         
19434         if(value.length < 1)  {
19435             if(this.allowBlank){
19436                 return true;
19437             }
19438             return false;
19439         }
19440         
19441         if(value.length < this.minLength){
19442             return false;
19443         }
19444         if(value.length > this.maxLength){
19445             return false;
19446         }
19447         if(this.vtype){
19448             var vt = Roo.form.VTypes;
19449             if(!vt[this.vtype](value, this)){
19450                 return false;
19451             }
19452         }
19453         if(typeof this.validator == "function"){
19454             var msg = this.validator(value);
19455             if(msg !== true){
19456                 return false;
19457             }
19458         }
19459         
19460         if(this.regex && !this.regex.test(value)){
19461             return false;
19462         }
19463         
19464         if(typeof(this.parseDate(value)) == 'undefined'){
19465             return false;
19466         }
19467         
19468         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19469             return false;
19470         }      
19471         
19472         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19473             return false;
19474         } 
19475         
19476         
19477         return true;
19478     },
19479     
19480     reset : function()
19481     {
19482         this.date = this.viewDate = '';
19483         
19484         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19485     }
19486    
19487 });
19488
19489 Roo.apply(Roo.bootstrap.DateField,  {
19490     
19491     head : {
19492         tag: 'thead',
19493         cn: [
19494         {
19495             tag: 'tr',
19496             cn: [
19497             {
19498                 tag: 'th',
19499                 cls: 'prev',
19500                 html: '<i class="fa fa-arrow-left"/>'
19501             },
19502             {
19503                 tag: 'th',
19504                 cls: 'switch',
19505                 colspan: '5'
19506             },
19507             {
19508                 tag: 'th',
19509                 cls: 'next',
19510                 html: '<i class="fa fa-arrow-right"/>'
19511             }
19512
19513             ]
19514         }
19515         ]
19516     },
19517     
19518     content : {
19519         tag: 'tbody',
19520         cn: [
19521         {
19522             tag: 'tr',
19523             cn: [
19524             {
19525                 tag: 'td',
19526                 colspan: '7'
19527             }
19528             ]
19529         }
19530         ]
19531     },
19532     
19533     footer : {
19534         tag: 'tfoot',
19535         cn: [
19536         {
19537             tag: 'tr',
19538             cn: [
19539             {
19540                 tag: 'th',
19541                 colspan: '7',
19542                 cls: 'today'
19543             }
19544                     
19545             ]
19546         }
19547         ]
19548     },
19549     
19550     dates:{
19551         en: {
19552             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19553             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19554             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19555             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19556             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19557             today: "Today"
19558         }
19559     },
19560     
19561     modes: [
19562     {
19563         clsName: 'days',
19564         navFnc: 'Month',
19565         navStep: 1
19566     },
19567     {
19568         clsName: 'months',
19569         navFnc: 'FullYear',
19570         navStep: 1
19571     },
19572     {
19573         clsName: 'years',
19574         navFnc: 'FullYear',
19575         navStep: 10
19576     }]
19577 });
19578
19579 Roo.apply(Roo.bootstrap.DateField,  {
19580   
19581     template : {
19582         tag: 'div',
19583         cls: 'datepicker dropdown-menu roo-dynamic',
19584         cn: [
19585         {
19586             tag: 'div',
19587             cls: 'datepicker-days',
19588             cn: [
19589             {
19590                 tag: 'table',
19591                 cls: 'table-condensed',
19592                 cn:[
19593                 Roo.bootstrap.DateField.head,
19594                 {
19595                     tag: 'tbody'
19596                 },
19597                 Roo.bootstrap.DateField.footer
19598                 ]
19599             }
19600             ]
19601         },
19602         {
19603             tag: 'div',
19604             cls: 'datepicker-months',
19605             cn: [
19606             {
19607                 tag: 'table',
19608                 cls: 'table-condensed',
19609                 cn:[
19610                 Roo.bootstrap.DateField.head,
19611                 Roo.bootstrap.DateField.content,
19612                 Roo.bootstrap.DateField.footer
19613                 ]
19614             }
19615             ]
19616         },
19617         {
19618             tag: 'div',
19619             cls: 'datepicker-years',
19620             cn: [
19621             {
19622                 tag: 'table',
19623                 cls: 'table-condensed',
19624                 cn:[
19625                 Roo.bootstrap.DateField.head,
19626                 Roo.bootstrap.DateField.content,
19627                 Roo.bootstrap.DateField.footer
19628                 ]
19629             }
19630             ]
19631         }
19632         ]
19633     }
19634 });
19635
19636  
19637
19638  /*
19639  * - LGPL
19640  *
19641  * TimeField
19642  * 
19643  */
19644
19645 /**
19646  * @class Roo.bootstrap.TimeField
19647  * @extends Roo.bootstrap.Input
19648  * Bootstrap DateField class
19649  * 
19650  * 
19651  * @constructor
19652  * Create a new TimeField
19653  * @param {Object} config The config object
19654  */
19655
19656 Roo.bootstrap.TimeField = function(config){
19657     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19658     this.addEvents({
19659             /**
19660              * @event show
19661              * Fires when this field show.
19662              * @param {Roo.bootstrap.DateField} thisthis
19663              * @param {Mixed} date The date value
19664              */
19665             show : true,
19666             /**
19667              * @event show
19668              * Fires when this field hide.
19669              * @param {Roo.bootstrap.DateField} this
19670              * @param {Mixed} date The date value
19671              */
19672             hide : true,
19673             /**
19674              * @event select
19675              * Fires when select a date.
19676              * @param {Roo.bootstrap.DateField} this
19677              * @param {Mixed} date The date value
19678              */
19679             select : true
19680         });
19681 };
19682
19683 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19684     
19685     /**
19686      * @cfg {String} format
19687      * The default time format string which can be overriden for localization support.  The format must be
19688      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19689      */
19690     format : "H:i",
19691        
19692     onRender: function(ct, position)
19693     {
19694         
19695         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19696                 
19697         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19698         
19699         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19700         
19701         this.pop = this.picker().select('>.datepicker-time',true).first();
19702         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19703         
19704         this.picker().on('mousedown', this.onMousedown, this);
19705         this.picker().on('click', this.onClick, this);
19706         
19707         this.picker().addClass('datepicker-dropdown');
19708     
19709         this.fillTime();
19710         this.update();
19711             
19712         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19713         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19714         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19715         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19716         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19717         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19718
19719     },
19720     
19721     fireKey: function(e){
19722         if (!this.picker().isVisible()){
19723             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19724                 this.show();
19725             }
19726             return;
19727         }
19728
19729         e.preventDefault();
19730         
19731         switch(e.keyCode){
19732             case 27: // escape
19733                 this.hide();
19734                 break;
19735             case 37: // left
19736             case 39: // right
19737                 this.onTogglePeriod();
19738                 break;
19739             case 38: // up
19740                 this.onIncrementMinutes();
19741                 break;
19742             case 40: // down
19743                 this.onDecrementMinutes();
19744                 break;
19745             case 13: // enter
19746             case 9: // tab
19747                 this.setTime();
19748                 break;
19749         }
19750     },
19751     
19752     onClick: function(e) {
19753         e.stopPropagation();
19754         e.preventDefault();
19755     },
19756     
19757     picker : function()
19758     {
19759         return this.el.select('.datepicker', true).first();
19760     },
19761     
19762     fillTime: function()
19763     {    
19764         var time = this.pop.select('tbody', true).first();
19765         
19766         time.dom.innerHTML = '';
19767         
19768         time.createChild({
19769             tag: 'tr',
19770             cn: [
19771                 {
19772                     tag: 'td',
19773                     cn: [
19774                         {
19775                             tag: 'a',
19776                             href: '#',
19777                             cls: 'btn',
19778                             cn: [
19779                                 {
19780                                     tag: 'span',
19781                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19782                                 }
19783                             ]
19784                         } 
19785                     ]
19786                 },
19787                 {
19788                     tag: 'td',
19789                     cls: 'separator'
19790                 },
19791                 {
19792                     tag: 'td',
19793                     cn: [
19794                         {
19795                             tag: 'a',
19796                             href: '#',
19797                             cls: 'btn',
19798                             cn: [
19799                                 {
19800                                     tag: 'span',
19801                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19802                                 }
19803                             ]
19804                         }
19805                     ]
19806                 },
19807                 {
19808                     tag: 'td',
19809                     cls: 'separator'
19810                 }
19811             ]
19812         });
19813         
19814         time.createChild({
19815             tag: 'tr',
19816             cn: [
19817                 {
19818                     tag: 'td',
19819                     cn: [
19820                         {
19821                             tag: 'span',
19822                             cls: 'timepicker-hour',
19823                             html: '00'
19824                         }  
19825                     ]
19826                 },
19827                 {
19828                     tag: 'td',
19829                     cls: 'separator',
19830                     html: ':'
19831                 },
19832                 {
19833                     tag: 'td',
19834                     cn: [
19835                         {
19836                             tag: 'span',
19837                             cls: 'timepicker-minute',
19838                             html: '00'
19839                         }  
19840                     ]
19841                 },
19842                 {
19843                     tag: 'td',
19844                     cls: 'separator'
19845                 },
19846                 {
19847                     tag: 'td',
19848                     cn: [
19849                         {
19850                             tag: 'button',
19851                             type: 'button',
19852                             cls: 'btn btn-primary period',
19853                             html: 'AM'
19854                             
19855                         }
19856                     ]
19857                 }
19858             ]
19859         });
19860         
19861         time.createChild({
19862             tag: 'tr',
19863             cn: [
19864                 {
19865                     tag: 'td',
19866                     cn: [
19867                         {
19868                             tag: 'a',
19869                             href: '#',
19870                             cls: 'btn',
19871                             cn: [
19872                                 {
19873                                     tag: 'span',
19874                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19875                                 }
19876                             ]
19877                         }
19878                     ]
19879                 },
19880                 {
19881                     tag: 'td',
19882                     cls: 'separator'
19883                 },
19884                 {
19885                     tag: 'td',
19886                     cn: [
19887                         {
19888                             tag: 'a',
19889                             href: '#',
19890                             cls: 'btn',
19891                             cn: [
19892                                 {
19893                                     tag: 'span',
19894                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19895                                 }
19896                             ]
19897                         }
19898                     ]
19899                 },
19900                 {
19901                     tag: 'td',
19902                     cls: 'separator'
19903                 }
19904             ]
19905         });
19906         
19907     },
19908     
19909     update: function()
19910     {
19911         
19912         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19913         
19914         this.fill();
19915     },
19916     
19917     fill: function() 
19918     {
19919         var hours = this.time.getHours();
19920         var minutes = this.time.getMinutes();
19921         var period = 'AM';
19922         
19923         if(hours > 11){
19924             period = 'PM';
19925         }
19926         
19927         if(hours == 0){
19928             hours = 12;
19929         }
19930         
19931         
19932         if(hours > 12){
19933             hours = hours - 12;
19934         }
19935         
19936         if(hours < 10){
19937             hours = '0' + hours;
19938         }
19939         
19940         if(minutes < 10){
19941             minutes = '0' + minutes;
19942         }
19943         
19944         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19945         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19946         this.pop.select('button', true).first().dom.innerHTML = period;
19947         
19948     },
19949     
19950     place: function()
19951     {   
19952         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19953         
19954         var cls = ['bottom'];
19955         
19956         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19957             cls.pop();
19958             cls.push('top');
19959         }
19960         
19961         cls.push('right');
19962         
19963         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19964             cls.pop();
19965             cls.push('left');
19966         }
19967         
19968         this.picker().addClass(cls.join('-'));
19969         
19970         var _this = this;
19971         
19972         Roo.each(cls, function(c){
19973             if(c == 'bottom'){
19974                 _this.picker().setTop(_this.inputEl().getHeight());
19975                 return;
19976             }
19977             if(c == 'top'){
19978                 _this.picker().setTop(0 - _this.picker().getHeight());
19979                 return;
19980             }
19981             
19982             if(c == 'left'){
19983                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19984                 return;
19985             }
19986             if(c == 'right'){
19987                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19988                 return;
19989             }
19990         });
19991         
19992     },
19993   
19994     onFocus : function()
19995     {
19996         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19997         this.show();
19998     },
19999     
20000     onBlur : function()
20001     {
20002         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20003         this.hide();
20004     },
20005     
20006     show : function()
20007     {
20008         this.picker().show();
20009         this.pop.show();
20010         this.update();
20011         this.place();
20012         
20013         this.fireEvent('show', this, this.date);
20014     },
20015     
20016     hide : function()
20017     {
20018         this.picker().hide();
20019         this.pop.hide();
20020         
20021         this.fireEvent('hide', this, this.date);
20022     },
20023     
20024     setTime : function()
20025     {
20026         this.hide();
20027         this.setValue(this.time.format(this.format));
20028         
20029         this.fireEvent('select', this, this.date);
20030         
20031         
20032     },
20033     
20034     onMousedown: function(e){
20035         e.stopPropagation();
20036         e.preventDefault();
20037     },
20038     
20039     onIncrementHours: function()
20040     {
20041         Roo.log('onIncrementHours');
20042         this.time = this.time.add(Date.HOUR, 1);
20043         this.update();
20044         
20045     },
20046     
20047     onDecrementHours: function()
20048     {
20049         Roo.log('onDecrementHours');
20050         this.time = this.time.add(Date.HOUR, -1);
20051         this.update();
20052     },
20053     
20054     onIncrementMinutes: function()
20055     {
20056         Roo.log('onIncrementMinutes');
20057         this.time = this.time.add(Date.MINUTE, 1);
20058         this.update();
20059     },
20060     
20061     onDecrementMinutes: function()
20062     {
20063         Roo.log('onDecrementMinutes');
20064         this.time = this.time.add(Date.MINUTE, -1);
20065         this.update();
20066     },
20067     
20068     onTogglePeriod: function()
20069     {
20070         Roo.log('onTogglePeriod');
20071         this.time = this.time.add(Date.HOUR, 12);
20072         this.update();
20073     }
20074     
20075    
20076 });
20077
20078 Roo.apply(Roo.bootstrap.TimeField,  {
20079     
20080     content : {
20081         tag: 'tbody',
20082         cn: [
20083             {
20084                 tag: 'tr',
20085                 cn: [
20086                 {
20087                     tag: 'td',
20088                     colspan: '7'
20089                 }
20090                 ]
20091             }
20092         ]
20093     },
20094     
20095     footer : {
20096         tag: 'tfoot',
20097         cn: [
20098             {
20099                 tag: 'tr',
20100                 cn: [
20101                 {
20102                     tag: 'th',
20103                     colspan: '7',
20104                     cls: '',
20105                     cn: [
20106                         {
20107                             tag: 'button',
20108                             cls: 'btn btn-info ok',
20109                             html: 'OK'
20110                         }
20111                     ]
20112                 }
20113
20114                 ]
20115             }
20116         ]
20117     }
20118 });
20119
20120 Roo.apply(Roo.bootstrap.TimeField,  {
20121   
20122     template : {
20123         tag: 'div',
20124         cls: 'datepicker dropdown-menu',
20125         cn: [
20126             {
20127                 tag: 'div',
20128                 cls: 'datepicker-time',
20129                 cn: [
20130                 {
20131                     tag: 'table',
20132                     cls: 'table-condensed',
20133                     cn:[
20134                     Roo.bootstrap.TimeField.content,
20135                     Roo.bootstrap.TimeField.footer
20136                     ]
20137                 }
20138                 ]
20139             }
20140         ]
20141     }
20142 });
20143
20144  
20145
20146  /*
20147  * - LGPL
20148  *
20149  * MonthField
20150  * 
20151  */
20152
20153 /**
20154  * @class Roo.bootstrap.MonthField
20155  * @extends Roo.bootstrap.Input
20156  * Bootstrap MonthField class
20157  * 
20158  * @cfg {String} language default en
20159  * 
20160  * @constructor
20161  * Create a new MonthField
20162  * @param {Object} config The config object
20163  */
20164
20165 Roo.bootstrap.MonthField = function(config){
20166     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20167     
20168     this.addEvents({
20169         /**
20170          * @event show
20171          * Fires when this field show.
20172          * @param {Roo.bootstrap.MonthField} this
20173          * @param {Mixed} date The date value
20174          */
20175         show : true,
20176         /**
20177          * @event show
20178          * Fires when this field hide.
20179          * @param {Roo.bootstrap.MonthField} this
20180          * @param {Mixed} date The date value
20181          */
20182         hide : true,
20183         /**
20184          * @event select
20185          * Fires when select a date.
20186          * @param {Roo.bootstrap.MonthField} this
20187          * @param {String} oldvalue The old value
20188          * @param {String} newvalue The new value
20189          */
20190         select : true
20191     });
20192 };
20193
20194 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20195     
20196     onRender: function(ct, position)
20197     {
20198         
20199         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20200         
20201         this.language = this.language || 'en';
20202         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20203         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20204         
20205         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20206         this.isInline = false;
20207         this.isInput = true;
20208         this.component = this.el.select('.add-on', true).first() || false;
20209         this.component = (this.component && this.component.length === 0) ? false : this.component;
20210         this.hasInput = this.component && this.inputEL().length;
20211         
20212         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20213         
20214         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20215         
20216         this.picker().on('mousedown', this.onMousedown, this);
20217         this.picker().on('click', this.onClick, this);
20218         
20219         this.picker().addClass('datepicker-dropdown');
20220         
20221         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20222             v.setStyle('width', '189px');
20223         });
20224         
20225         this.fillMonths();
20226         
20227         this.update();
20228         
20229         if(this.isInline) {
20230             this.show();
20231         }
20232         
20233     },
20234     
20235     setValue: function(v, suppressEvent)
20236     {   
20237         var o = this.getValue();
20238         
20239         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20240         
20241         this.update();
20242
20243         if(suppressEvent !== true){
20244             this.fireEvent('select', this, o, v);
20245         }
20246         
20247     },
20248     
20249     getValue: function()
20250     {
20251         return this.value;
20252     },
20253     
20254     onClick: function(e) 
20255     {
20256         e.stopPropagation();
20257         e.preventDefault();
20258         
20259         var target = e.getTarget();
20260         
20261         if(target.nodeName.toLowerCase() === 'i'){
20262             target = Roo.get(target).dom.parentNode;
20263         }
20264         
20265         var nodeName = target.nodeName;
20266         var className = target.className;
20267         var html = target.innerHTML;
20268         
20269         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20270             return;
20271         }
20272         
20273         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20274         
20275         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20276         
20277         this.hide();
20278                         
20279     },
20280     
20281     picker : function()
20282     {
20283         return this.pickerEl;
20284     },
20285     
20286     fillMonths: function()
20287     {    
20288         var i = 0;
20289         var months = this.picker().select('>.datepicker-months td', true).first();
20290         
20291         months.dom.innerHTML = '';
20292         
20293         while (i < 12) {
20294             var month = {
20295                 tag: 'span',
20296                 cls: 'month',
20297                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20298             };
20299             
20300             months.createChild(month);
20301         }
20302         
20303     },
20304     
20305     update: function()
20306     {
20307         var _this = this;
20308         
20309         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20310             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20311         }
20312         
20313         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20314             e.removeClass('active');
20315             
20316             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20317                 e.addClass('active');
20318             }
20319         })
20320     },
20321     
20322     place: function()
20323     {
20324         if(this.isInline) {
20325             return;
20326         }
20327         
20328         this.picker().removeClass(['bottom', 'top']);
20329         
20330         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20331             /*
20332              * place to the top of element!
20333              *
20334              */
20335             
20336             this.picker().addClass('top');
20337             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20338             
20339             return;
20340         }
20341         
20342         this.picker().addClass('bottom');
20343         
20344         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20345     },
20346     
20347     onFocus : function()
20348     {
20349         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20350         this.show();
20351     },
20352     
20353     onBlur : function()
20354     {
20355         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20356         
20357         var d = this.inputEl().getValue();
20358         
20359         this.setValue(d);
20360                 
20361         this.hide();
20362     },
20363     
20364     show : function()
20365     {
20366         this.picker().show();
20367         this.picker().select('>.datepicker-months', true).first().show();
20368         this.update();
20369         this.place();
20370         
20371         this.fireEvent('show', this, this.date);
20372     },
20373     
20374     hide : function()
20375     {
20376         if(this.isInline) {
20377             return;
20378         }
20379         this.picker().hide();
20380         this.fireEvent('hide', this, this.date);
20381         
20382     },
20383     
20384     onMousedown: function(e)
20385     {
20386         e.stopPropagation();
20387         e.preventDefault();
20388     },
20389     
20390     keyup: function(e)
20391     {
20392         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20393         this.update();
20394     },
20395
20396     fireKey: function(e)
20397     {
20398         if (!this.picker().isVisible()){
20399             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20400                 this.show();
20401             }
20402             return;
20403         }
20404         
20405         var dir;
20406         
20407         switch(e.keyCode){
20408             case 27: // escape
20409                 this.hide();
20410                 e.preventDefault();
20411                 break;
20412             case 37: // left
20413             case 39: // right
20414                 dir = e.keyCode == 37 ? -1 : 1;
20415                 
20416                 this.vIndex = this.vIndex + dir;
20417                 
20418                 if(this.vIndex < 0){
20419                     this.vIndex = 0;
20420                 }
20421                 
20422                 if(this.vIndex > 11){
20423                     this.vIndex = 11;
20424                 }
20425                 
20426                 if(isNaN(this.vIndex)){
20427                     this.vIndex = 0;
20428                 }
20429                 
20430                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20431                 
20432                 break;
20433             case 38: // up
20434             case 40: // down
20435                 
20436                 dir = e.keyCode == 38 ? -1 : 1;
20437                 
20438                 this.vIndex = this.vIndex + dir * 4;
20439                 
20440                 if(this.vIndex < 0){
20441                     this.vIndex = 0;
20442                 }
20443                 
20444                 if(this.vIndex > 11){
20445                     this.vIndex = 11;
20446                 }
20447                 
20448                 if(isNaN(this.vIndex)){
20449                     this.vIndex = 0;
20450                 }
20451                 
20452                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20453                 break;
20454                 
20455             case 13: // enter
20456                 
20457                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20458                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20459                 }
20460                 
20461                 this.hide();
20462                 e.preventDefault();
20463                 break;
20464             case 9: // tab
20465                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20466                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20467                 }
20468                 this.hide();
20469                 break;
20470             case 16: // shift
20471             case 17: // ctrl
20472             case 18: // alt
20473                 break;
20474             default :
20475                 this.hide();
20476                 
20477         }
20478     },
20479     
20480     remove: function() 
20481     {
20482         this.picker().remove();
20483     }
20484    
20485 });
20486
20487 Roo.apply(Roo.bootstrap.MonthField,  {
20488     
20489     content : {
20490         tag: 'tbody',
20491         cn: [
20492         {
20493             tag: 'tr',
20494             cn: [
20495             {
20496                 tag: 'td',
20497                 colspan: '7'
20498             }
20499             ]
20500         }
20501         ]
20502     },
20503     
20504     dates:{
20505         en: {
20506             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20507             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20508         }
20509     }
20510 });
20511
20512 Roo.apply(Roo.bootstrap.MonthField,  {
20513   
20514     template : {
20515         tag: 'div',
20516         cls: 'datepicker dropdown-menu roo-dynamic',
20517         cn: [
20518             {
20519                 tag: 'div',
20520                 cls: 'datepicker-months',
20521                 cn: [
20522                 {
20523                     tag: 'table',
20524                     cls: 'table-condensed',
20525                     cn:[
20526                         Roo.bootstrap.DateField.content
20527                     ]
20528                 }
20529                 ]
20530             }
20531         ]
20532     }
20533 });
20534
20535  
20536
20537  
20538  /*
20539  * - LGPL
20540  *
20541  * CheckBox
20542  * 
20543  */
20544
20545 /**
20546  * @class Roo.bootstrap.CheckBox
20547  * @extends Roo.bootstrap.Input
20548  * Bootstrap CheckBox class
20549  * 
20550  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20551  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20552  * @cfg {String} boxLabel The text that appears beside the checkbox
20553  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20554  * @cfg {Boolean} checked initnal the element
20555  * @cfg {Boolean} inline inline the element (default false)
20556  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20557  * @cfg {String} tooltip label tooltip
20558  * 
20559  * @constructor
20560  * Create a new CheckBox
20561  * @param {Object} config The config object
20562  */
20563
20564 Roo.bootstrap.CheckBox = function(config){
20565     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20566    
20567     this.addEvents({
20568         /**
20569         * @event check
20570         * Fires when the element is checked or unchecked.
20571         * @param {Roo.bootstrap.CheckBox} this This input
20572         * @param {Boolean} checked The new checked value
20573         */
20574        check : true,
20575        /**
20576         * @event click
20577         * Fires when the element is click.
20578         * @param {Roo.bootstrap.CheckBox} this This input
20579         */
20580        click : true
20581     });
20582     
20583 };
20584
20585 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20586   
20587     inputType: 'checkbox',
20588     inputValue: 1,
20589     valueOff: 0,
20590     boxLabel: false,
20591     checked: false,
20592     weight : false,
20593     inline: false,
20594     tooltip : '',
20595     
20596     getAutoCreate : function()
20597     {
20598         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20599         
20600         var id = Roo.id();
20601         
20602         var cfg = {};
20603         
20604         cfg.cls = 'form-group ' + this.inputType; //input-group
20605         
20606         if(this.inline){
20607             cfg.cls += ' ' + this.inputType + '-inline';
20608         }
20609         
20610         var input =  {
20611             tag: 'input',
20612             id : id,
20613             type : this.inputType,
20614             value : this.inputValue,
20615             cls : 'roo-' + this.inputType, //'form-box',
20616             placeholder : this.placeholder || ''
20617             
20618         };
20619         
20620         if(this.inputType != 'radio'){
20621             var hidden =  {
20622                 tag: 'input',
20623                 type : 'hidden',
20624                 cls : 'roo-hidden-value',
20625                 value : this.checked ? this.inputValue : this.valueOff
20626             };
20627         }
20628         
20629             
20630         if (this.weight) { // Validity check?
20631             cfg.cls += " " + this.inputType + "-" + this.weight;
20632         }
20633         
20634         if (this.disabled) {
20635             input.disabled=true;
20636         }
20637         
20638         if(this.checked){
20639             input.checked = this.checked;
20640         }
20641         
20642         if (this.name) {
20643             
20644             input.name = this.name;
20645             
20646             if(this.inputType != 'radio'){
20647                 hidden.name = this.name;
20648                 input.name = '_hidden_' + this.name;
20649             }
20650         }
20651         
20652         if (this.size) {
20653             input.cls += ' input-' + this.size;
20654         }
20655         
20656         var settings=this;
20657         
20658         ['xs','sm','md','lg'].map(function(size){
20659             if (settings[size]) {
20660                 cfg.cls += ' col-' + size + '-' + settings[size];
20661             }
20662         });
20663         
20664         var inputblock = input;
20665          
20666         if (this.before || this.after) {
20667             
20668             inputblock = {
20669                 cls : 'input-group',
20670                 cn :  [] 
20671             };
20672             
20673             if (this.before) {
20674                 inputblock.cn.push({
20675                     tag :'span',
20676                     cls : 'input-group-addon',
20677                     html : this.before
20678                 });
20679             }
20680             
20681             inputblock.cn.push(input);
20682             
20683             if(this.inputType != 'radio'){
20684                 inputblock.cn.push(hidden);
20685             }
20686             
20687             if (this.after) {
20688                 inputblock.cn.push({
20689                     tag :'span',
20690                     cls : 'input-group-addon',
20691                     html : this.after
20692                 });
20693             }
20694             
20695         }
20696         
20697         if (align ==='left' && this.fieldLabel.length) {
20698 //                Roo.log("left and has label");
20699             cfg.cn = [
20700                 {
20701                     tag: 'label',
20702                     'for' :  id,
20703                     cls : 'control-label',
20704                     html : this.fieldLabel
20705                 },
20706                 {
20707                     cls : "", 
20708                     cn: [
20709                         inputblock
20710                     ]
20711                 }
20712             ];
20713             
20714             if(this.labelWidth > 12){
20715                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20716             }
20717             
20718             if(this.labelWidth < 13 && this.labelmd == 0){
20719                 this.labelmd = this.labelWidth;
20720             }
20721             
20722             if(this.labellg > 0){
20723                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20724                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20725             }
20726             
20727             if(this.labelmd > 0){
20728                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20729                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20730             }
20731             
20732             if(this.labelsm > 0){
20733                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20734                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20735             }
20736             
20737             if(this.labelxs > 0){
20738                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20739                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20740             }
20741             
20742         } else if ( this.fieldLabel.length) {
20743 //                Roo.log(" label");
20744                 cfg.cn = [
20745                    
20746                     {
20747                         tag: this.boxLabel ? 'span' : 'label',
20748                         'for': id,
20749                         cls: 'control-label box-input-label',
20750                         //cls : 'input-group-addon',
20751                         html : this.fieldLabel
20752                     },
20753                     
20754                     inputblock
20755                     
20756                 ];
20757
20758         } else {
20759             
20760 //                Roo.log(" no label && no align");
20761                 cfg.cn = [  inputblock ] ;
20762                 
20763                 
20764         }
20765         
20766         if(this.boxLabel){
20767              var boxLabelCfg = {
20768                 tag: 'label',
20769                 //'for': id, // box label is handled by onclick - so no for...
20770                 cls: 'box-label',
20771                 html: this.boxLabel
20772             };
20773             
20774             if(this.tooltip){
20775                 boxLabelCfg.tooltip = this.tooltip;
20776             }
20777              
20778             cfg.cn.push(boxLabelCfg);
20779         }
20780         
20781         if(this.inputType != 'radio'){
20782             cfg.cn.push(hidden);
20783         }
20784         
20785         return cfg;
20786         
20787     },
20788     
20789     /**
20790      * return the real input element.
20791      */
20792     inputEl: function ()
20793     {
20794         return this.el.select('input.roo-' + this.inputType,true).first();
20795     },
20796     hiddenEl: function ()
20797     {
20798         return this.el.select('input.roo-hidden-value',true).first();
20799     },
20800     
20801     labelEl: function()
20802     {
20803         return this.el.select('label.control-label',true).first();
20804     },
20805     /* depricated... */
20806     
20807     label: function()
20808     {
20809         return this.labelEl();
20810     },
20811     
20812     boxLabelEl: function()
20813     {
20814         return this.el.select('label.box-label',true).first();
20815     },
20816     
20817     initEvents : function()
20818     {
20819 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20820         
20821         this.inputEl().on('click', this.onClick,  this);
20822         
20823         if (this.boxLabel) { 
20824             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20825         }
20826         
20827         this.startValue = this.getValue();
20828         
20829         if(this.groupId){
20830             Roo.bootstrap.CheckBox.register(this);
20831         }
20832     },
20833     
20834     onClick : function(e)
20835     {   
20836         if(this.fireEvent('click', this, e) !== false){
20837             this.setChecked(!this.checked);
20838         }
20839         
20840     },
20841     
20842     setChecked : function(state,suppressEvent)
20843     {
20844         this.startValue = this.getValue();
20845
20846         if(this.inputType == 'radio'){
20847             
20848             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20849                 e.dom.checked = false;
20850             });
20851             
20852             this.inputEl().dom.checked = true;
20853             
20854             this.inputEl().dom.value = this.inputValue;
20855             
20856             if(suppressEvent !== true){
20857                 this.fireEvent('check', this, true);
20858             }
20859             
20860             this.validate();
20861             
20862             return;
20863         }
20864         
20865         this.checked = state;
20866         
20867         this.inputEl().dom.checked = state;
20868         
20869         
20870         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20871         
20872         if(suppressEvent !== true){
20873             this.fireEvent('check', this, state);
20874         }
20875         
20876         this.validate();
20877     },
20878     
20879     getValue : function()
20880     {
20881         if(this.inputType == 'radio'){
20882             return this.getGroupValue();
20883         }
20884         
20885         return this.hiddenEl().dom.value;
20886         
20887     },
20888     
20889     getGroupValue : function()
20890     {
20891         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20892             return '';
20893         }
20894         
20895         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20896     },
20897     
20898     setValue : function(v,suppressEvent)
20899     {
20900         if(this.inputType == 'radio'){
20901             this.setGroupValue(v, suppressEvent);
20902             return;
20903         }
20904         
20905         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20906         
20907         this.validate();
20908     },
20909     
20910     setGroupValue : function(v, suppressEvent)
20911     {
20912         this.startValue = this.getValue();
20913         
20914         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20915             e.dom.checked = false;
20916             
20917             if(e.dom.value == v){
20918                 e.dom.checked = true;
20919             }
20920         });
20921         
20922         if(suppressEvent !== true){
20923             this.fireEvent('check', this, true);
20924         }
20925
20926         this.validate();
20927         
20928         return;
20929     },
20930     
20931     validate : function()
20932     {
20933         if(this.getVisibilityEl().hasClass('hidden')){
20934             return true;
20935         }
20936         
20937         if(
20938                 this.disabled || 
20939                 (this.inputType == 'radio' && this.validateRadio()) ||
20940                 (this.inputType == 'checkbox' && this.validateCheckbox())
20941         ){
20942             this.markValid();
20943             return true;
20944         }
20945         
20946         this.markInvalid();
20947         return false;
20948     },
20949     
20950     validateRadio : function()
20951     {
20952         if(this.getVisibilityEl().hasClass('hidden')){
20953             return true;
20954         }
20955         
20956         if(this.allowBlank){
20957             return true;
20958         }
20959         
20960         var valid = false;
20961         
20962         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20963             if(!e.dom.checked){
20964                 return;
20965             }
20966             
20967             valid = true;
20968             
20969             return false;
20970         });
20971         
20972         return valid;
20973     },
20974     
20975     validateCheckbox : function()
20976     {
20977         if(!this.groupId){
20978             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20979             //return (this.getValue() == this.inputValue) ? true : false;
20980         }
20981         
20982         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20983         
20984         if(!group){
20985             return false;
20986         }
20987         
20988         var r = false;
20989         
20990         for(var i in group){
20991             if(group[i].el.isVisible(true)){
20992                 r = false;
20993                 break;
20994             }
20995             
20996             r = true;
20997         }
20998         
20999         for(var i in group){
21000             if(r){
21001                 break;
21002             }
21003             
21004             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21005         }
21006         
21007         return r;
21008     },
21009     
21010     /**
21011      * Mark this field as valid
21012      */
21013     markValid : function()
21014     {
21015         var _this = this;
21016         
21017         this.fireEvent('valid', this);
21018         
21019         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21020         
21021         if(this.groupId){
21022             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21023         }
21024         
21025         if(label){
21026             label.markValid();
21027         }
21028
21029         if(this.inputType == 'radio'){
21030             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21031                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21032                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21033             });
21034             
21035             return;
21036         }
21037
21038         if(!this.groupId){
21039             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21040             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21041             return;
21042         }
21043         
21044         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21045         
21046         if(!group){
21047             return;
21048         }
21049         
21050         for(var i in group){
21051             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21052             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21053         }
21054     },
21055     
21056      /**
21057      * Mark this field as invalid
21058      * @param {String} msg The validation message
21059      */
21060     markInvalid : function(msg)
21061     {
21062         if(this.allowBlank){
21063             return;
21064         }
21065         
21066         var _this = this;
21067         
21068         this.fireEvent('invalid', this, msg);
21069         
21070         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21071         
21072         if(this.groupId){
21073             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21074         }
21075         
21076         if(label){
21077             label.markInvalid();
21078         }
21079             
21080         if(this.inputType == 'radio'){
21081             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21082                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21083                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21084             });
21085             
21086             return;
21087         }
21088         
21089         if(!this.groupId){
21090             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21091             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21092             return;
21093         }
21094         
21095         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21096         
21097         if(!group){
21098             return;
21099         }
21100         
21101         for(var i in group){
21102             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21103             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21104         }
21105         
21106     },
21107     
21108     clearInvalid : function()
21109     {
21110         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21111         
21112         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21113         
21114         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21115         
21116         if (label && label.iconEl) {
21117             label.iconEl.removeClass(label.validClass);
21118             label.iconEl.removeClass(label.invalidClass);
21119         }
21120     },
21121     
21122     disable : function()
21123     {
21124         if(this.inputType != 'radio'){
21125             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21126             return;
21127         }
21128         
21129         var _this = this;
21130         
21131         if(this.rendered){
21132             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21133                 _this.getActionEl().addClass(this.disabledClass);
21134                 e.dom.disabled = true;
21135             });
21136         }
21137         
21138         this.disabled = true;
21139         this.fireEvent("disable", this);
21140         return this;
21141     },
21142
21143     enable : function()
21144     {
21145         if(this.inputType != 'radio'){
21146             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21147             return;
21148         }
21149         
21150         var _this = this;
21151         
21152         if(this.rendered){
21153             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21154                 _this.getActionEl().removeClass(this.disabledClass);
21155                 e.dom.disabled = false;
21156             });
21157         }
21158         
21159         this.disabled = false;
21160         this.fireEvent("enable", this);
21161         return this;
21162     },
21163     
21164     setBoxLabel : function(v)
21165     {
21166         this.boxLabel = v;
21167         
21168         if(this.rendered){
21169             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21170         }
21171     }
21172
21173 });
21174
21175 Roo.apply(Roo.bootstrap.CheckBox, {
21176     
21177     groups: {},
21178     
21179      /**
21180     * register a CheckBox Group
21181     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21182     */
21183     register : function(checkbox)
21184     {
21185         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21186             this.groups[checkbox.groupId] = {};
21187         }
21188         
21189         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21190             return;
21191         }
21192         
21193         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21194         
21195     },
21196     /**
21197     * fetch a CheckBox Group based on the group ID
21198     * @param {string} the group ID
21199     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21200     */
21201     get: function(groupId) {
21202         if (typeof(this.groups[groupId]) == 'undefined') {
21203             return false;
21204         }
21205         
21206         return this.groups[groupId] ;
21207     }
21208     
21209     
21210 });
21211 /*
21212  * - LGPL
21213  *
21214  * RadioItem
21215  * 
21216  */
21217
21218 /**
21219  * @class Roo.bootstrap.Radio
21220  * @extends Roo.bootstrap.Component
21221  * Bootstrap Radio class
21222  * @cfg {String} boxLabel - the label associated
21223  * @cfg {String} value - the value of radio
21224  * 
21225  * @constructor
21226  * Create a new Radio
21227  * @param {Object} config The config object
21228  */
21229 Roo.bootstrap.Radio = function(config){
21230     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21231     
21232 };
21233
21234 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21235     
21236     boxLabel : '',
21237     
21238     value : '',
21239     
21240     getAutoCreate : function()
21241     {
21242         var cfg = {
21243             tag : 'div',
21244             cls : 'form-group radio',
21245             cn : [
21246                 {
21247                     tag : 'label',
21248                     cls : 'box-label',
21249                     html : this.boxLabel
21250                 }
21251             ]
21252         };
21253         
21254         return cfg;
21255     },
21256     
21257     initEvents : function() 
21258     {
21259         this.parent().register(this);
21260         
21261         this.el.on('click', this.onClick, this);
21262         
21263     },
21264     
21265     onClick : function(e)
21266     {
21267         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21268             this.setChecked(true);
21269         }
21270     },
21271     
21272     setChecked : function(state, suppressEvent)
21273     {
21274         this.parent().setValue(this.value, suppressEvent);
21275         
21276     },
21277     
21278     setBoxLabel : function(v)
21279     {
21280         this.boxLabel = v;
21281         
21282         if(this.rendered){
21283             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21284         }
21285     }
21286     
21287 });
21288  
21289
21290  /*
21291  * - LGPL
21292  *
21293  * Input
21294  * 
21295  */
21296
21297 /**
21298  * @class Roo.bootstrap.SecurePass
21299  * @extends Roo.bootstrap.Input
21300  * Bootstrap SecurePass class
21301  *
21302  * 
21303  * @constructor
21304  * Create a new SecurePass
21305  * @param {Object} config The config object
21306  */
21307  
21308 Roo.bootstrap.SecurePass = function (config) {
21309     // these go here, so the translation tool can replace them..
21310     this.errors = {
21311         PwdEmpty: "Please type a password, and then retype it to confirm.",
21312         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21313         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21314         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21315         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21316         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21317         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21318         TooWeak: "Your password is Too Weak."
21319     },
21320     this.meterLabel = "Password strength:";
21321     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21322     this.meterClass = [
21323         "roo-password-meter-tooweak", 
21324         "roo-password-meter-weak", 
21325         "roo-password-meter-medium", 
21326         "roo-password-meter-strong", 
21327         "roo-password-meter-grey"
21328     ];
21329     
21330     this.errors = {};
21331     
21332     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21333 }
21334
21335 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21336     /**
21337      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21338      * {
21339      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21340      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21341      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21342      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21343      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21344      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21345      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21346      * })
21347      */
21348     // private
21349     
21350     meterWidth: 300,
21351     errorMsg :'',    
21352     errors: false,
21353     imageRoot: '/',
21354     /**
21355      * @cfg {String/Object} Label for the strength meter (defaults to
21356      * 'Password strength:')
21357      */
21358     // private
21359     meterLabel: '',
21360     /**
21361      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21362      * ['Weak', 'Medium', 'Strong'])
21363      */
21364     // private    
21365     pwdStrengths: false,    
21366     // private
21367     strength: 0,
21368     // private
21369     _lastPwd: null,
21370     // private
21371     kCapitalLetter: 0,
21372     kSmallLetter: 1,
21373     kDigit: 2,
21374     kPunctuation: 3,
21375     
21376     insecure: false,
21377     // private
21378     initEvents: function ()
21379     {
21380         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21381
21382         if (this.el.is('input[type=password]') && Roo.isSafari) {
21383             this.el.on('keydown', this.SafariOnKeyDown, this);
21384         }
21385
21386         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21387     },
21388     // private
21389     onRender: function (ct, position)
21390     {
21391         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21392         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21393         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21394
21395         this.trigger.createChild({
21396                    cn: [
21397                     {
21398                     //id: 'PwdMeter',
21399                     tag: 'div',
21400                     cls: 'roo-password-meter-grey col-xs-12',
21401                     style: {
21402                         //width: 0,
21403                         //width: this.meterWidth + 'px'                                                
21404                         }
21405                     },
21406                     {                            
21407                          cls: 'roo-password-meter-text'                          
21408                     }
21409                 ]            
21410         });
21411
21412          
21413         if (this.hideTrigger) {
21414             this.trigger.setDisplayed(false);
21415         }
21416         this.setSize(this.width || '', this.height || '');
21417     },
21418     // private
21419     onDestroy: function ()
21420     {
21421         if (this.trigger) {
21422             this.trigger.removeAllListeners();
21423             this.trigger.remove();
21424         }
21425         if (this.wrap) {
21426             this.wrap.remove();
21427         }
21428         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21429     },
21430     // private
21431     checkStrength: function ()
21432     {
21433         var pwd = this.inputEl().getValue();
21434         if (pwd == this._lastPwd) {
21435             return;
21436         }
21437
21438         var strength;
21439         if (this.ClientSideStrongPassword(pwd)) {
21440             strength = 3;
21441         } else if (this.ClientSideMediumPassword(pwd)) {
21442             strength = 2;
21443         } else if (this.ClientSideWeakPassword(pwd)) {
21444             strength = 1;
21445         } else {
21446             strength = 0;
21447         }
21448         
21449         Roo.log('strength1: ' + strength);
21450         
21451         //var pm = this.trigger.child('div/div/div').dom;
21452         var pm = this.trigger.child('div/div');
21453         pm.removeClass(this.meterClass);
21454         pm.addClass(this.meterClass[strength]);
21455                 
21456         
21457         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21458                 
21459         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21460         
21461         this._lastPwd = pwd;
21462     },
21463     reset: function ()
21464     {
21465         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21466         
21467         this._lastPwd = '';
21468         
21469         var pm = this.trigger.child('div/div');
21470         pm.removeClass(this.meterClass);
21471         pm.addClass('roo-password-meter-grey');        
21472         
21473         
21474         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21475         
21476         pt.innerHTML = '';
21477         this.inputEl().dom.type='password';
21478     },
21479     // private
21480     validateValue: function (value)
21481     {
21482         
21483         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21484             return false;
21485         }
21486         if (value.length == 0) {
21487             if (this.allowBlank) {
21488                 this.clearInvalid();
21489                 return true;
21490             }
21491
21492             this.markInvalid(this.errors.PwdEmpty);
21493             this.errorMsg = this.errors.PwdEmpty;
21494             return false;
21495         }
21496         
21497         if(this.insecure){
21498             return true;
21499         }
21500         
21501         if ('[\x21-\x7e]*'.match(value)) {
21502             this.markInvalid(this.errors.PwdBadChar);
21503             this.errorMsg = this.errors.PwdBadChar;
21504             return false;
21505         }
21506         if (value.length < 6) {
21507             this.markInvalid(this.errors.PwdShort);
21508             this.errorMsg = this.errors.PwdShort;
21509             return false;
21510         }
21511         if (value.length > 16) {
21512             this.markInvalid(this.errors.PwdLong);
21513             this.errorMsg = this.errors.PwdLong;
21514             return false;
21515         }
21516         var strength;
21517         if (this.ClientSideStrongPassword(value)) {
21518             strength = 3;
21519         } else if (this.ClientSideMediumPassword(value)) {
21520             strength = 2;
21521         } else if (this.ClientSideWeakPassword(value)) {
21522             strength = 1;
21523         } else {
21524             strength = 0;
21525         }
21526
21527         
21528         if (strength < 2) {
21529             //this.markInvalid(this.errors.TooWeak);
21530             this.errorMsg = this.errors.TooWeak;
21531             //return false;
21532         }
21533         
21534         
21535         console.log('strength2: ' + strength);
21536         
21537         //var pm = this.trigger.child('div/div/div').dom;
21538         
21539         var pm = this.trigger.child('div/div');
21540         pm.removeClass(this.meterClass);
21541         pm.addClass(this.meterClass[strength]);
21542                 
21543         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21544                 
21545         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21546         
21547         this.errorMsg = ''; 
21548         return true;
21549     },
21550     // private
21551     CharacterSetChecks: function (type)
21552     {
21553         this.type = type;
21554         this.fResult = false;
21555     },
21556     // private
21557     isctype: function (character, type)
21558     {
21559         switch (type) {  
21560             case this.kCapitalLetter:
21561                 if (character >= 'A' && character <= 'Z') {
21562                     return true;
21563                 }
21564                 break;
21565             
21566             case this.kSmallLetter:
21567                 if (character >= 'a' && character <= 'z') {
21568                     return true;
21569                 }
21570                 break;
21571             
21572             case this.kDigit:
21573                 if (character >= '0' && character <= '9') {
21574                     return true;
21575                 }
21576                 break;
21577             
21578             case this.kPunctuation:
21579                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21580                     return true;
21581                 }
21582                 break;
21583             
21584             default:
21585                 return false;
21586         }
21587
21588     },
21589     // private
21590     IsLongEnough: function (pwd, size)
21591     {
21592         return !(pwd == null || isNaN(size) || pwd.length < size);
21593     },
21594     // private
21595     SpansEnoughCharacterSets: function (word, nb)
21596     {
21597         if (!this.IsLongEnough(word, nb))
21598         {
21599             return false;
21600         }
21601
21602         var characterSetChecks = new Array(
21603             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21604             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21605         );
21606         
21607         for (var index = 0; index < word.length; ++index) {
21608             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21609                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21610                     characterSetChecks[nCharSet].fResult = true;
21611                     break;
21612                 }
21613             }
21614         }
21615
21616         var nCharSets = 0;
21617         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21618             if (characterSetChecks[nCharSet].fResult) {
21619                 ++nCharSets;
21620             }
21621         }
21622
21623         if (nCharSets < nb) {
21624             return false;
21625         }
21626         return true;
21627     },
21628     // private
21629     ClientSideStrongPassword: function (pwd)
21630     {
21631         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21632     },
21633     // private
21634     ClientSideMediumPassword: function (pwd)
21635     {
21636         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21637     },
21638     // private
21639     ClientSideWeakPassword: function (pwd)
21640     {
21641         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21642     }
21643           
21644 })//<script type="text/javascript">
21645
21646 /*
21647  * Based  Ext JS Library 1.1.1
21648  * Copyright(c) 2006-2007, Ext JS, LLC.
21649  * LGPL
21650  *
21651  */
21652  
21653 /**
21654  * @class Roo.HtmlEditorCore
21655  * @extends Roo.Component
21656  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21657  *
21658  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21659  */
21660
21661 Roo.HtmlEditorCore = function(config){
21662     
21663     
21664     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21665     
21666     
21667     this.addEvents({
21668         /**
21669          * @event initialize
21670          * Fires when the editor is fully initialized (including the iframe)
21671          * @param {Roo.HtmlEditorCore} this
21672          */
21673         initialize: true,
21674         /**
21675          * @event activate
21676          * Fires when the editor is first receives the focus. Any insertion must wait
21677          * until after this event.
21678          * @param {Roo.HtmlEditorCore} this
21679          */
21680         activate: true,
21681          /**
21682          * @event beforesync
21683          * Fires before the textarea is updated with content from the editor iframe. Return false
21684          * to cancel the sync.
21685          * @param {Roo.HtmlEditorCore} this
21686          * @param {String} html
21687          */
21688         beforesync: true,
21689          /**
21690          * @event beforepush
21691          * Fires before the iframe editor is updated with content from the textarea. Return false
21692          * to cancel the push.
21693          * @param {Roo.HtmlEditorCore} this
21694          * @param {String} html
21695          */
21696         beforepush: true,
21697          /**
21698          * @event sync
21699          * Fires when the textarea is updated with content from the editor iframe.
21700          * @param {Roo.HtmlEditorCore} this
21701          * @param {String} html
21702          */
21703         sync: true,
21704          /**
21705          * @event push
21706          * Fires when the iframe editor is updated with content from the textarea.
21707          * @param {Roo.HtmlEditorCore} this
21708          * @param {String} html
21709          */
21710         push: true,
21711         
21712         /**
21713          * @event editorevent
21714          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21715          * @param {Roo.HtmlEditorCore} this
21716          */
21717         editorevent: true
21718         
21719     });
21720     
21721     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21722     
21723     // defaults : white / black...
21724     this.applyBlacklists();
21725     
21726     
21727     
21728 };
21729
21730
21731 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21732
21733
21734      /**
21735      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21736      */
21737     
21738     owner : false,
21739     
21740      /**
21741      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21742      *                        Roo.resizable.
21743      */
21744     resizable : false,
21745      /**
21746      * @cfg {Number} height (in pixels)
21747      */   
21748     height: 300,
21749    /**
21750      * @cfg {Number} width (in pixels)
21751      */   
21752     width: 500,
21753     
21754     /**
21755      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21756      * 
21757      */
21758     stylesheets: false,
21759     
21760     // id of frame..
21761     frameId: false,
21762     
21763     // private properties
21764     validationEvent : false,
21765     deferHeight: true,
21766     initialized : false,
21767     activated : false,
21768     sourceEditMode : false,
21769     onFocus : Roo.emptyFn,
21770     iframePad:3,
21771     hideMode:'offsets',
21772     
21773     clearUp: true,
21774     
21775     // blacklist + whitelisted elements..
21776     black: false,
21777     white: false,
21778      
21779     bodyCls : '',
21780
21781     /**
21782      * Protected method that will not generally be called directly. It
21783      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21784      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21785      */
21786     getDocMarkup : function(){
21787         // body styles..
21788         var st = '';
21789         
21790         // inherit styels from page...?? 
21791         if (this.stylesheets === false) {
21792             
21793             Roo.get(document.head).select('style').each(function(node) {
21794                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21795             });
21796             
21797             Roo.get(document.head).select('link').each(function(node) { 
21798                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21799             });
21800             
21801         } else if (!this.stylesheets.length) {
21802                 // simple..
21803                 st = '<style type="text/css">' +
21804                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21805                    '</style>';
21806         } else { 
21807             st = '<style type="text/css">' +
21808                     this.stylesheets +
21809                 '</style>';
21810         }
21811         
21812         st +=  '<style type="text/css">' +
21813             'IMG { cursor: pointer } ' +
21814         '</style>';
21815
21816         var cls = 'roo-htmleditor-body';
21817         
21818         if(this.bodyCls.length){
21819             cls += ' ' + this.bodyCls;
21820         }
21821         
21822         return '<html><head>' + st  +
21823             //<style type="text/css">' +
21824             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21825             //'</style>' +
21826             ' </head><body class="' +  cls + '"></body></html>';
21827     },
21828
21829     // private
21830     onRender : function(ct, position)
21831     {
21832         var _t = this;
21833         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21834         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21835         
21836         
21837         this.el.dom.style.border = '0 none';
21838         this.el.dom.setAttribute('tabIndex', -1);
21839         this.el.addClass('x-hidden hide');
21840         
21841         
21842         
21843         if(Roo.isIE){ // fix IE 1px bogus margin
21844             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21845         }
21846        
21847         
21848         this.frameId = Roo.id();
21849         
21850          
21851         
21852         var iframe = this.owner.wrap.createChild({
21853             tag: 'iframe',
21854             cls: 'form-control', // bootstrap..
21855             id: this.frameId,
21856             name: this.frameId,
21857             frameBorder : 'no',
21858             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21859         }, this.el
21860         );
21861         
21862         
21863         this.iframe = iframe.dom;
21864
21865          this.assignDocWin();
21866         
21867         this.doc.designMode = 'on';
21868        
21869         this.doc.open();
21870         this.doc.write(this.getDocMarkup());
21871         this.doc.close();
21872
21873         
21874         var task = { // must defer to wait for browser to be ready
21875             run : function(){
21876                 //console.log("run task?" + this.doc.readyState);
21877                 this.assignDocWin();
21878                 if(this.doc.body || this.doc.readyState == 'complete'){
21879                     try {
21880                         this.doc.designMode="on";
21881                     } catch (e) {
21882                         return;
21883                     }
21884                     Roo.TaskMgr.stop(task);
21885                     this.initEditor.defer(10, this);
21886                 }
21887             },
21888             interval : 10,
21889             duration: 10000,
21890             scope: this
21891         };
21892         Roo.TaskMgr.start(task);
21893
21894     },
21895
21896     // private
21897     onResize : function(w, h)
21898     {
21899          Roo.log('resize: ' +w + ',' + h );
21900         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21901         if(!this.iframe){
21902             return;
21903         }
21904         if(typeof w == 'number'){
21905             
21906             this.iframe.style.width = w + 'px';
21907         }
21908         if(typeof h == 'number'){
21909             
21910             this.iframe.style.height = h + 'px';
21911             if(this.doc){
21912                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21913             }
21914         }
21915         
21916     },
21917
21918     /**
21919      * Toggles the editor between standard and source edit mode.
21920      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21921      */
21922     toggleSourceEdit : function(sourceEditMode){
21923         
21924         this.sourceEditMode = sourceEditMode === true;
21925         
21926         if(this.sourceEditMode){
21927  
21928             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21929             
21930         }else{
21931             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21932             //this.iframe.className = '';
21933             this.deferFocus();
21934         }
21935         //this.setSize(this.owner.wrap.getSize());
21936         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21937     },
21938
21939     
21940   
21941
21942     /**
21943      * Protected method that will not generally be called directly. If you need/want
21944      * custom HTML cleanup, this is the method you should override.
21945      * @param {String} html The HTML to be cleaned
21946      * return {String} The cleaned HTML
21947      */
21948     cleanHtml : function(html){
21949         html = String(html);
21950         if(html.length > 5){
21951             if(Roo.isSafari){ // strip safari nonsense
21952                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21953             }
21954         }
21955         if(html == '&nbsp;'){
21956             html = '';
21957         }
21958         return html;
21959     },
21960
21961     /**
21962      * HTML Editor -> Textarea
21963      * Protected method that will not generally be called directly. Syncs the contents
21964      * of the editor iframe with the textarea.
21965      */
21966     syncValue : function(){
21967         if(this.initialized){
21968             var bd = (this.doc.body || this.doc.documentElement);
21969             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21970             var html = bd.innerHTML;
21971             if(Roo.isSafari){
21972                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21973                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21974                 if(m && m[1]){
21975                     html = '<div style="'+m[0]+'">' + html + '</div>';
21976                 }
21977             }
21978             html = this.cleanHtml(html);
21979             // fix up the special chars.. normaly like back quotes in word...
21980             // however we do not want to do this with chinese..
21981             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21982                 var cc = b.charCodeAt();
21983                 if (
21984                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21985                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21986                     (cc >= 0xf900 && cc < 0xfb00 )
21987                 ) {
21988                         return b;
21989                 }
21990                 return "&#"+cc+";" 
21991             });
21992             if(this.owner.fireEvent('beforesync', this, html) !== false){
21993                 this.el.dom.value = html;
21994                 this.owner.fireEvent('sync', this, html);
21995             }
21996         }
21997     },
21998
21999     /**
22000      * Protected method that will not generally be called directly. Pushes the value of the textarea
22001      * into the iframe editor.
22002      */
22003     pushValue : function(){
22004         if(this.initialized){
22005             var v = this.el.dom.value.trim();
22006             
22007 //            if(v.length < 1){
22008 //                v = '&#160;';
22009 //            }
22010             
22011             if(this.owner.fireEvent('beforepush', this, v) !== false){
22012                 var d = (this.doc.body || this.doc.documentElement);
22013                 d.innerHTML = v;
22014                 this.cleanUpPaste();
22015                 this.el.dom.value = d.innerHTML;
22016                 this.owner.fireEvent('push', this, v);
22017             }
22018         }
22019     },
22020
22021     // private
22022     deferFocus : function(){
22023         this.focus.defer(10, this);
22024     },
22025
22026     // doc'ed in Field
22027     focus : function(){
22028         if(this.win && !this.sourceEditMode){
22029             this.win.focus();
22030         }else{
22031             this.el.focus();
22032         }
22033     },
22034     
22035     assignDocWin: function()
22036     {
22037         var iframe = this.iframe;
22038         
22039          if(Roo.isIE){
22040             this.doc = iframe.contentWindow.document;
22041             this.win = iframe.contentWindow;
22042         } else {
22043 //            if (!Roo.get(this.frameId)) {
22044 //                return;
22045 //            }
22046 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22047 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22048             
22049             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22050                 return;
22051             }
22052             
22053             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22054             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22055         }
22056     },
22057     
22058     // private
22059     initEditor : function(){
22060         //console.log("INIT EDITOR");
22061         this.assignDocWin();
22062         
22063         
22064         
22065         this.doc.designMode="on";
22066         this.doc.open();
22067         this.doc.write(this.getDocMarkup());
22068         this.doc.close();
22069         
22070         var dbody = (this.doc.body || this.doc.documentElement);
22071         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22072         // this copies styles from the containing element into thsi one..
22073         // not sure why we need all of this..
22074         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22075         
22076         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22077         //ss['background-attachment'] = 'fixed'; // w3c
22078         dbody.bgProperties = 'fixed'; // ie
22079         //Roo.DomHelper.applyStyles(dbody, ss);
22080         Roo.EventManager.on(this.doc, {
22081             //'mousedown': this.onEditorEvent,
22082             'mouseup': this.onEditorEvent,
22083             'dblclick': this.onEditorEvent,
22084             'click': this.onEditorEvent,
22085             'keyup': this.onEditorEvent,
22086             buffer:100,
22087             scope: this
22088         });
22089         if(Roo.isGecko){
22090             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22091         }
22092         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22093             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22094         }
22095         this.initialized = true;
22096
22097         this.owner.fireEvent('initialize', this);
22098         this.pushValue();
22099     },
22100
22101     // private
22102     onDestroy : function(){
22103         
22104         
22105         
22106         if(this.rendered){
22107             
22108             //for (var i =0; i < this.toolbars.length;i++) {
22109             //    // fixme - ask toolbars for heights?
22110             //    this.toolbars[i].onDestroy();
22111            // }
22112             
22113             //this.wrap.dom.innerHTML = '';
22114             //this.wrap.remove();
22115         }
22116     },
22117
22118     // private
22119     onFirstFocus : function(){
22120         
22121         this.assignDocWin();
22122         
22123         
22124         this.activated = true;
22125          
22126     
22127         if(Roo.isGecko){ // prevent silly gecko errors
22128             this.win.focus();
22129             var s = this.win.getSelection();
22130             if(!s.focusNode || s.focusNode.nodeType != 3){
22131                 var r = s.getRangeAt(0);
22132                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22133                 r.collapse(true);
22134                 this.deferFocus();
22135             }
22136             try{
22137                 this.execCmd('useCSS', true);
22138                 this.execCmd('styleWithCSS', false);
22139             }catch(e){}
22140         }
22141         this.owner.fireEvent('activate', this);
22142     },
22143
22144     // private
22145     adjustFont: function(btn){
22146         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22147         //if(Roo.isSafari){ // safari
22148         //    adjust *= 2;
22149        // }
22150         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22151         if(Roo.isSafari){ // safari
22152             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22153             v =  (v < 10) ? 10 : v;
22154             v =  (v > 48) ? 48 : v;
22155             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22156             
22157         }
22158         
22159         
22160         v = Math.max(1, v+adjust);
22161         
22162         this.execCmd('FontSize', v  );
22163     },
22164
22165     onEditorEvent : function(e)
22166     {
22167         this.owner.fireEvent('editorevent', this, e);
22168       //  this.updateToolbar();
22169         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22170     },
22171
22172     insertTag : function(tg)
22173     {
22174         // could be a bit smarter... -> wrap the current selected tRoo..
22175         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22176             
22177             range = this.createRange(this.getSelection());
22178             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22179             wrappingNode.appendChild(range.extractContents());
22180             range.insertNode(wrappingNode);
22181
22182             return;
22183             
22184             
22185             
22186         }
22187         this.execCmd("formatblock",   tg);
22188         
22189     },
22190     
22191     insertText : function(txt)
22192     {
22193         
22194         
22195         var range = this.createRange();
22196         range.deleteContents();
22197                //alert(Sender.getAttribute('label'));
22198                
22199         range.insertNode(this.doc.createTextNode(txt));
22200     } ,
22201     
22202      
22203
22204     /**
22205      * Executes a Midas editor command on the editor document and performs necessary focus and
22206      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22207      * @param {String} cmd The Midas command
22208      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22209      */
22210     relayCmd : function(cmd, value){
22211         this.win.focus();
22212         this.execCmd(cmd, value);
22213         this.owner.fireEvent('editorevent', this);
22214         //this.updateToolbar();
22215         this.owner.deferFocus();
22216     },
22217
22218     /**
22219      * Executes a Midas editor command directly on the editor document.
22220      * For visual commands, you should use {@link #relayCmd} instead.
22221      * <b>This should only be called after the editor is initialized.</b>
22222      * @param {String} cmd The Midas command
22223      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22224      */
22225     execCmd : function(cmd, value){
22226         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22227         this.syncValue();
22228     },
22229  
22230  
22231    
22232     /**
22233      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22234      * to insert tRoo.
22235      * @param {String} text | dom node.. 
22236      */
22237     insertAtCursor : function(text)
22238     {
22239         
22240         if(!this.activated){
22241             return;
22242         }
22243         /*
22244         if(Roo.isIE){
22245             this.win.focus();
22246             var r = this.doc.selection.createRange();
22247             if(r){
22248                 r.collapse(true);
22249                 r.pasteHTML(text);
22250                 this.syncValue();
22251                 this.deferFocus();
22252             
22253             }
22254             return;
22255         }
22256         */
22257         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22258             this.win.focus();
22259             
22260             
22261             // from jquery ui (MIT licenced)
22262             var range, node;
22263             var win = this.win;
22264             
22265             if (win.getSelection && win.getSelection().getRangeAt) {
22266                 range = win.getSelection().getRangeAt(0);
22267                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22268                 range.insertNode(node);
22269             } else if (win.document.selection && win.document.selection.createRange) {
22270                 // no firefox support
22271                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22272                 win.document.selection.createRange().pasteHTML(txt);
22273             } else {
22274                 // no firefox support
22275                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22276                 this.execCmd('InsertHTML', txt);
22277             } 
22278             
22279             this.syncValue();
22280             
22281             this.deferFocus();
22282         }
22283     },
22284  // private
22285     mozKeyPress : function(e){
22286         if(e.ctrlKey){
22287             var c = e.getCharCode(), cmd;
22288           
22289             if(c > 0){
22290                 c = String.fromCharCode(c).toLowerCase();
22291                 switch(c){
22292                     case 'b':
22293                         cmd = 'bold';
22294                         break;
22295                     case 'i':
22296                         cmd = 'italic';
22297                         break;
22298                     
22299                     case 'u':
22300                         cmd = 'underline';
22301                         break;
22302                     
22303                     case 'v':
22304                         this.cleanUpPaste.defer(100, this);
22305                         return;
22306                         
22307                 }
22308                 if(cmd){
22309                     this.win.focus();
22310                     this.execCmd(cmd);
22311                     this.deferFocus();
22312                     e.preventDefault();
22313                 }
22314                 
22315             }
22316         }
22317     },
22318
22319     // private
22320     fixKeys : function(){ // load time branching for fastest keydown performance
22321         if(Roo.isIE){
22322             return function(e){
22323                 var k = e.getKey(), r;
22324                 if(k == e.TAB){
22325                     e.stopEvent();
22326                     r = this.doc.selection.createRange();
22327                     if(r){
22328                         r.collapse(true);
22329                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22330                         this.deferFocus();
22331                     }
22332                     return;
22333                 }
22334                 
22335                 if(k == e.ENTER){
22336                     r = this.doc.selection.createRange();
22337                     if(r){
22338                         var target = r.parentElement();
22339                         if(!target || target.tagName.toLowerCase() != 'li'){
22340                             e.stopEvent();
22341                             r.pasteHTML('<br />');
22342                             r.collapse(false);
22343                             r.select();
22344                         }
22345                     }
22346                 }
22347                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22348                     this.cleanUpPaste.defer(100, this);
22349                     return;
22350                 }
22351                 
22352                 
22353             };
22354         }else if(Roo.isOpera){
22355             return function(e){
22356                 var k = e.getKey();
22357                 if(k == e.TAB){
22358                     e.stopEvent();
22359                     this.win.focus();
22360                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22361                     this.deferFocus();
22362                 }
22363                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22364                     this.cleanUpPaste.defer(100, this);
22365                     return;
22366                 }
22367                 
22368             };
22369         }else if(Roo.isSafari){
22370             return function(e){
22371                 var k = e.getKey();
22372                 
22373                 if(k == e.TAB){
22374                     e.stopEvent();
22375                     this.execCmd('InsertText','\t');
22376                     this.deferFocus();
22377                     return;
22378                 }
22379                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22380                     this.cleanUpPaste.defer(100, this);
22381                     return;
22382                 }
22383                 
22384              };
22385         }
22386     }(),
22387     
22388     getAllAncestors: function()
22389     {
22390         var p = this.getSelectedNode();
22391         var a = [];
22392         if (!p) {
22393             a.push(p); // push blank onto stack..
22394             p = this.getParentElement();
22395         }
22396         
22397         
22398         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22399             a.push(p);
22400             p = p.parentNode;
22401         }
22402         a.push(this.doc.body);
22403         return a;
22404     },
22405     lastSel : false,
22406     lastSelNode : false,
22407     
22408     
22409     getSelection : function() 
22410     {
22411         this.assignDocWin();
22412         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22413     },
22414     
22415     getSelectedNode: function() 
22416     {
22417         // this may only work on Gecko!!!
22418         
22419         // should we cache this!!!!
22420         
22421         
22422         
22423          
22424         var range = this.createRange(this.getSelection()).cloneRange();
22425         
22426         if (Roo.isIE) {
22427             var parent = range.parentElement();
22428             while (true) {
22429                 var testRange = range.duplicate();
22430                 testRange.moveToElementText(parent);
22431                 if (testRange.inRange(range)) {
22432                     break;
22433                 }
22434                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22435                     break;
22436                 }
22437                 parent = parent.parentElement;
22438             }
22439             return parent;
22440         }
22441         
22442         // is ancestor a text element.
22443         var ac =  range.commonAncestorContainer;
22444         if (ac.nodeType == 3) {
22445             ac = ac.parentNode;
22446         }
22447         
22448         var ar = ac.childNodes;
22449          
22450         var nodes = [];
22451         var other_nodes = [];
22452         var has_other_nodes = false;
22453         for (var i=0;i<ar.length;i++) {
22454             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22455                 continue;
22456             }
22457             // fullly contained node.
22458             
22459             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22460                 nodes.push(ar[i]);
22461                 continue;
22462             }
22463             
22464             // probably selected..
22465             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22466                 other_nodes.push(ar[i]);
22467                 continue;
22468             }
22469             // outer..
22470             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22471                 continue;
22472             }
22473             
22474             
22475             has_other_nodes = true;
22476         }
22477         if (!nodes.length && other_nodes.length) {
22478             nodes= other_nodes;
22479         }
22480         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22481             return false;
22482         }
22483         
22484         return nodes[0];
22485     },
22486     createRange: function(sel)
22487     {
22488         // this has strange effects when using with 
22489         // top toolbar - not sure if it's a great idea.
22490         //this.editor.contentWindow.focus();
22491         if (typeof sel != "undefined") {
22492             try {
22493                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22494             } catch(e) {
22495                 return this.doc.createRange();
22496             }
22497         } else {
22498             return this.doc.createRange();
22499         }
22500     },
22501     getParentElement: function()
22502     {
22503         
22504         this.assignDocWin();
22505         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22506         
22507         var range = this.createRange(sel);
22508          
22509         try {
22510             var p = range.commonAncestorContainer;
22511             while (p.nodeType == 3) { // text node
22512                 p = p.parentNode;
22513             }
22514             return p;
22515         } catch (e) {
22516             return null;
22517         }
22518     
22519     },
22520     /***
22521      *
22522      * Range intersection.. the hard stuff...
22523      *  '-1' = before
22524      *  '0' = hits..
22525      *  '1' = after.
22526      *         [ -- selected range --- ]
22527      *   [fail]                        [fail]
22528      *
22529      *    basically..
22530      *      if end is before start or  hits it. fail.
22531      *      if start is after end or hits it fail.
22532      *
22533      *   if either hits (but other is outside. - then it's not 
22534      *   
22535      *    
22536      **/
22537     
22538     
22539     // @see http://www.thismuchiknow.co.uk/?p=64.
22540     rangeIntersectsNode : function(range, node)
22541     {
22542         var nodeRange = node.ownerDocument.createRange();
22543         try {
22544             nodeRange.selectNode(node);
22545         } catch (e) {
22546             nodeRange.selectNodeContents(node);
22547         }
22548     
22549         var rangeStartRange = range.cloneRange();
22550         rangeStartRange.collapse(true);
22551     
22552         var rangeEndRange = range.cloneRange();
22553         rangeEndRange.collapse(false);
22554     
22555         var nodeStartRange = nodeRange.cloneRange();
22556         nodeStartRange.collapse(true);
22557     
22558         var nodeEndRange = nodeRange.cloneRange();
22559         nodeEndRange.collapse(false);
22560     
22561         return rangeStartRange.compareBoundaryPoints(
22562                  Range.START_TO_START, nodeEndRange) == -1 &&
22563                rangeEndRange.compareBoundaryPoints(
22564                  Range.START_TO_START, nodeStartRange) == 1;
22565         
22566          
22567     },
22568     rangeCompareNode : function(range, node)
22569     {
22570         var nodeRange = node.ownerDocument.createRange();
22571         try {
22572             nodeRange.selectNode(node);
22573         } catch (e) {
22574             nodeRange.selectNodeContents(node);
22575         }
22576         
22577         
22578         range.collapse(true);
22579     
22580         nodeRange.collapse(true);
22581      
22582         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22583         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22584          
22585         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22586         
22587         var nodeIsBefore   =  ss == 1;
22588         var nodeIsAfter    = ee == -1;
22589         
22590         if (nodeIsBefore && nodeIsAfter) {
22591             return 0; // outer
22592         }
22593         if (!nodeIsBefore && nodeIsAfter) {
22594             return 1; //right trailed.
22595         }
22596         
22597         if (nodeIsBefore && !nodeIsAfter) {
22598             return 2;  // left trailed.
22599         }
22600         // fully contined.
22601         return 3;
22602     },
22603
22604     // private? - in a new class?
22605     cleanUpPaste :  function()
22606     {
22607         // cleans up the whole document..
22608         Roo.log('cleanuppaste');
22609         
22610         this.cleanUpChildren(this.doc.body);
22611         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22612         if (clean != this.doc.body.innerHTML) {
22613             this.doc.body.innerHTML = clean;
22614         }
22615         
22616     },
22617     
22618     cleanWordChars : function(input) {// change the chars to hex code
22619         var he = Roo.HtmlEditorCore;
22620         
22621         var output = input;
22622         Roo.each(he.swapCodes, function(sw) { 
22623             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22624             
22625             output = output.replace(swapper, sw[1]);
22626         });
22627         
22628         return output;
22629     },
22630     
22631     
22632     cleanUpChildren : function (n)
22633     {
22634         if (!n.childNodes.length) {
22635             return;
22636         }
22637         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22638            this.cleanUpChild(n.childNodes[i]);
22639         }
22640     },
22641     
22642     
22643         
22644     
22645     cleanUpChild : function (node)
22646     {
22647         var ed = this;
22648         //console.log(node);
22649         if (node.nodeName == "#text") {
22650             // clean up silly Windows -- stuff?
22651             return; 
22652         }
22653         if (node.nodeName == "#comment") {
22654             node.parentNode.removeChild(node);
22655             // clean up silly Windows -- stuff?
22656             return; 
22657         }
22658         var lcname = node.tagName.toLowerCase();
22659         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22660         // whitelist of tags..
22661         
22662         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22663             // remove node.
22664             node.parentNode.removeChild(node);
22665             return;
22666             
22667         }
22668         
22669         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22670         
22671         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22672         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22673         
22674         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22675         //    remove_keep_children = true;
22676         //}
22677         
22678         if (remove_keep_children) {
22679             this.cleanUpChildren(node);
22680             // inserts everything just before this node...
22681             while (node.childNodes.length) {
22682                 var cn = node.childNodes[0];
22683                 node.removeChild(cn);
22684                 node.parentNode.insertBefore(cn, node);
22685             }
22686             node.parentNode.removeChild(node);
22687             return;
22688         }
22689         
22690         if (!node.attributes || !node.attributes.length) {
22691             this.cleanUpChildren(node);
22692             return;
22693         }
22694         
22695         function cleanAttr(n,v)
22696         {
22697             
22698             if (v.match(/^\./) || v.match(/^\//)) {
22699                 return;
22700             }
22701             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22702                 return;
22703             }
22704             if (v.match(/^#/)) {
22705                 return;
22706             }
22707 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22708             node.removeAttribute(n);
22709             
22710         }
22711         
22712         var cwhite = this.cwhite;
22713         var cblack = this.cblack;
22714             
22715         function cleanStyle(n,v)
22716         {
22717             if (v.match(/expression/)) { //XSS?? should we even bother..
22718                 node.removeAttribute(n);
22719                 return;
22720             }
22721             
22722             var parts = v.split(/;/);
22723             var clean = [];
22724             
22725             Roo.each(parts, function(p) {
22726                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22727                 if (!p.length) {
22728                     return true;
22729                 }
22730                 var l = p.split(':').shift().replace(/\s+/g,'');
22731                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22732                 
22733                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22734 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22735                     //node.removeAttribute(n);
22736                     return true;
22737                 }
22738                 //Roo.log()
22739                 // only allow 'c whitelisted system attributes'
22740                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22741 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22742                     //node.removeAttribute(n);
22743                     return true;
22744                 }
22745                 
22746                 
22747                  
22748                 
22749                 clean.push(p);
22750                 return true;
22751             });
22752             if (clean.length) { 
22753                 node.setAttribute(n, clean.join(';'));
22754             } else {
22755                 node.removeAttribute(n);
22756             }
22757             
22758         }
22759         
22760         
22761         for (var i = node.attributes.length-1; i > -1 ; i--) {
22762             var a = node.attributes[i];
22763             //console.log(a);
22764             
22765             if (a.name.toLowerCase().substr(0,2)=='on')  {
22766                 node.removeAttribute(a.name);
22767                 continue;
22768             }
22769             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22770                 node.removeAttribute(a.name);
22771                 continue;
22772             }
22773             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22774                 cleanAttr(a.name,a.value); // fixme..
22775                 continue;
22776             }
22777             if (a.name == 'style') {
22778                 cleanStyle(a.name,a.value);
22779                 continue;
22780             }
22781             /// clean up MS crap..
22782             // tecnically this should be a list of valid class'es..
22783             
22784             
22785             if (a.name == 'class') {
22786                 if (a.value.match(/^Mso/)) {
22787                     node.className = '';
22788                 }
22789                 
22790                 if (a.value.match(/^body$/)) {
22791                     node.className = '';
22792                 }
22793                 continue;
22794             }
22795             
22796             // style cleanup!?
22797             // class cleanup?
22798             
22799         }
22800         
22801         
22802         this.cleanUpChildren(node);
22803         
22804         
22805     },
22806     
22807     /**
22808      * Clean up MS wordisms...
22809      */
22810     cleanWord : function(node)
22811     {
22812         
22813         
22814         if (!node) {
22815             this.cleanWord(this.doc.body);
22816             return;
22817         }
22818         if (node.nodeName == "#text") {
22819             // clean up silly Windows -- stuff?
22820             return; 
22821         }
22822         if (node.nodeName == "#comment") {
22823             node.parentNode.removeChild(node);
22824             // clean up silly Windows -- stuff?
22825             return; 
22826         }
22827         
22828         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22829             node.parentNode.removeChild(node);
22830             return;
22831         }
22832         
22833         // remove - but keep children..
22834         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22835             while (node.childNodes.length) {
22836                 var cn = node.childNodes[0];
22837                 node.removeChild(cn);
22838                 node.parentNode.insertBefore(cn, node);
22839             }
22840             node.parentNode.removeChild(node);
22841             this.iterateChildren(node, this.cleanWord);
22842             return;
22843         }
22844         // clean styles
22845         if (node.className.length) {
22846             
22847             var cn = node.className.split(/\W+/);
22848             var cna = [];
22849             Roo.each(cn, function(cls) {
22850                 if (cls.match(/Mso[a-zA-Z]+/)) {
22851                     return;
22852                 }
22853                 cna.push(cls);
22854             });
22855             node.className = cna.length ? cna.join(' ') : '';
22856             if (!cna.length) {
22857                 node.removeAttribute("class");
22858             }
22859         }
22860         
22861         if (node.hasAttribute("lang")) {
22862             node.removeAttribute("lang");
22863         }
22864         
22865         if (node.hasAttribute("style")) {
22866             
22867             var styles = node.getAttribute("style").split(";");
22868             var nstyle = [];
22869             Roo.each(styles, function(s) {
22870                 if (!s.match(/:/)) {
22871                     return;
22872                 }
22873                 var kv = s.split(":");
22874                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22875                     return;
22876                 }
22877                 // what ever is left... we allow.
22878                 nstyle.push(s);
22879             });
22880             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22881             if (!nstyle.length) {
22882                 node.removeAttribute('style');
22883             }
22884         }
22885         this.iterateChildren(node, this.cleanWord);
22886         
22887         
22888         
22889     },
22890     /**
22891      * iterateChildren of a Node, calling fn each time, using this as the scole..
22892      * @param {DomNode} node node to iterate children of.
22893      * @param {Function} fn method of this class to call on each item.
22894      */
22895     iterateChildren : function(node, fn)
22896     {
22897         if (!node.childNodes.length) {
22898                 return;
22899         }
22900         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22901            fn.call(this, node.childNodes[i])
22902         }
22903     },
22904     
22905     
22906     /**
22907      * cleanTableWidths.
22908      *
22909      * Quite often pasting from word etc.. results in tables with column and widths.
22910      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22911      *
22912      */
22913     cleanTableWidths : function(node)
22914     {
22915          
22916          
22917         if (!node) {
22918             this.cleanTableWidths(this.doc.body);
22919             return;
22920         }
22921         
22922         // ignore list...
22923         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22924             return; 
22925         }
22926         Roo.log(node.tagName);
22927         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22928             this.iterateChildren(node, this.cleanTableWidths);
22929             return;
22930         }
22931         if (node.hasAttribute('width')) {
22932             node.removeAttribute('width');
22933         }
22934         
22935          
22936         if (node.hasAttribute("style")) {
22937             // pretty basic...
22938             
22939             var styles = node.getAttribute("style").split(";");
22940             var nstyle = [];
22941             Roo.each(styles, function(s) {
22942                 if (!s.match(/:/)) {
22943                     return;
22944                 }
22945                 var kv = s.split(":");
22946                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22947                     return;
22948                 }
22949                 // what ever is left... we allow.
22950                 nstyle.push(s);
22951             });
22952             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22953             if (!nstyle.length) {
22954                 node.removeAttribute('style');
22955             }
22956         }
22957         
22958         this.iterateChildren(node, this.cleanTableWidths);
22959         
22960         
22961     },
22962     
22963     
22964     
22965     
22966     domToHTML : function(currentElement, depth, nopadtext) {
22967         
22968         depth = depth || 0;
22969         nopadtext = nopadtext || false;
22970     
22971         if (!currentElement) {
22972             return this.domToHTML(this.doc.body);
22973         }
22974         
22975         //Roo.log(currentElement);
22976         var j;
22977         var allText = false;
22978         var nodeName = currentElement.nodeName;
22979         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22980         
22981         if  (nodeName == '#text') {
22982             
22983             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22984         }
22985         
22986         
22987         var ret = '';
22988         if (nodeName != 'BODY') {
22989              
22990             var i = 0;
22991             // Prints the node tagName, such as <A>, <IMG>, etc
22992             if (tagName) {
22993                 var attr = [];
22994                 for(i = 0; i < currentElement.attributes.length;i++) {
22995                     // quoting?
22996                     var aname = currentElement.attributes.item(i).name;
22997                     if (!currentElement.attributes.item(i).value.length) {
22998                         continue;
22999                     }
23000                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23001                 }
23002                 
23003                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23004             } 
23005             else {
23006                 
23007                 // eack
23008             }
23009         } else {
23010             tagName = false;
23011         }
23012         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23013             return ret;
23014         }
23015         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23016             nopadtext = true;
23017         }
23018         
23019         
23020         // Traverse the tree
23021         i = 0;
23022         var currentElementChild = currentElement.childNodes.item(i);
23023         var allText = true;
23024         var innerHTML  = '';
23025         lastnode = '';
23026         while (currentElementChild) {
23027             // Formatting code (indent the tree so it looks nice on the screen)
23028             var nopad = nopadtext;
23029             if (lastnode == 'SPAN') {
23030                 nopad  = true;
23031             }
23032             // text
23033             if  (currentElementChild.nodeName == '#text') {
23034                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23035                 toadd = nopadtext ? toadd : toadd.trim();
23036                 if (!nopad && toadd.length > 80) {
23037                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23038                 }
23039                 innerHTML  += toadd;
23040                 
23041                 i++;
23042                 currentElementChild = currentElement.childNodes.item(i);
23043                 lastNode = '';
23044                 continue;
23045             }
23046             allText = false;
23047             
23048             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23049                 
23050             // Recursively traverse the tree structure of the child node
23051             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23052             lastnode = currentElementChild.nodeName;
23053             i++;
23054             currentElementChild=currentElement.childNodes.item(i);
23055         }
23056         
23057         ret += innerHTML;
23058         
23059         if (!allText) {
23060                 // The remaining code is mostly for formatting the tree
23061             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23062         }
23063         
23064         
23065         if (tagName) {
23066             ret+= "</"+tagName+">";
23067         }
23068         return ret;
23069         
23070     },
23071         
23072     applyBlacklists : function()
23073     {
23074         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23075         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23076         
23077         this.white = [];
23078         this.black = [];
23079         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23080             if (b.indexOf(tag) > -1) {
23081                 return;
23082             }
23083             this.white.push(tag);
23084             
23085         }, this);
23086         
23087         Roo.each(w, function(tag) {
23088             if (b.indexOf(tag) > -1) {
23089                 return;
23090             }
23091             if (this.white.indexOf(tag) > -1) {
23092                 return;
23093             }
23094             this.white.push(tag);
23095             
23096         }, this);
23097         
23098         
23099         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23100             if (w.indexOf(tag) > -1) {
23101                 return;
23102             }
23103             this.black.push(tag);
23104             
23105         }, this);
23106         
23107         Roo.each(b, function(tag) {
23108             if (w.indexOf(tag) > -1) {
23109                 return;
23110             }
23111             if (this.black.indexOf(tag) > -1) {
23112                 return;
23113             }
23114             this.black.push(tag);
23115             
23116         }, this);
23117         
23118         
23119         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23120         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23121         
23122         this.cwhite = [];
23123         this.cblack = [];
23124         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23125             if (b.indexOf(tag) > -1) {
23126                 return;
23127             }
23128             this.cwhite.push(tag);
23129             
23130         }, this);
23131         
23132         Roo.each(w, function(tag) {
23133             if (b.indexOf(tag) > -1) {
23134                 return;
23135             }
23136             if (this.cwhite.indexOf(tag) > -1) {
23137                 return;
23138             }
23139             this.cwhite.push(tag);
23140             
23141         }, this);
23142         
23143         
23144         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23145             if (w.indexOf(tag) > -1) {
23146                 return;
23147             }
23148             this.cblack.push(tag);
23149             
23150         }, this);
23151         
23152         Roo.each(b, function(tag) {
23153             if (w.indexOf(tag) > -1) {
23154                 return;
23155             }
23156             if (this.cblack.indexOf(tag) > -1) {
23157                 return;
23158             }
23159             this.cblack.push(tag);
23160             
23161         }, this);
23162     },
23163     
23164     setStylesheets : function(stylesheets)
23165     {
23166         if(typeof(stylesheets) == 'string'){
23167             Roo.get(this.iframe.contentDocument.head).createChild({
23168                 tag : 'link',
23169                 rel : 'stylesheet',
23170                 type : 'text/css',
23171                 href : stylesheets
23172             });
23173             
23174             return;
23175         }
23176         var _this = this;
23177      
23178         Roo.each(stylesheets, function(s) {
23179             if(!s.length){
23180                 return;
23181             }
23182             
23183             Roo.get(_this.iframe.contentDocument.head).createChild({
23184                 tag : 'link',
23185                 rel : 'stylesheet',
23186                 type : 'text/css',
23187                 href : s
23188             });
23189         });
23190
23191         
23192     },
23193     
23194     removeStylesheets : function()
23195     {
23196         var _this = this;
23197         
23198         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23199             s.remove();
23200         });
23201     },
23202     
23203     setStyle : function(style)
23204     {
23205         Roo.get(this.iframe.contentDocument.head).createChild({
23206             tag : 'style',
23207             type : 'text/css',
23208             html : style
23209         });
23210
23211         return;
23212     }
23213     
23214     // hide stuff that is not compatible
23215     /**
23216      * @event blur
23217      * @hide
23218      */
23219     /**
23220      * @event change
23221      * @hide
23222      */
23223     /**
23224      * @event focus
23225      * @hide
23226      */
23227     /**
23228      * @event specialkey
23229      * @hide
23230      */
23231     /**
23232      * @cfg {String} fieldClass @hide
23233      */
23234     /**
23235      * @cfg {String} focusClass @hide
23236      */
23237     /**
23238      * @cfg {String} autoCreate @hide
23239      */
23240     /**
23241      * @cfg {String} inputType @hide
23242      */
23243     /**
23244      * @cfg {String} invalidClass @hide
23245      */
23246     /**
23247      * @cfg {String} invalidText @hide
23248      */
23249     /**
23250      * @cfg {String} msgFx @hide
23251      */
23252     /**
23253      * @cfg {String} validateOnBlur @hide
23254      */
23255 });
23256
23257 Roo.HtmlEditorCore.white = [
23258         'area', 'br', 'img', 'input', 'hr', 'wbr',
23259         
23260        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23261        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23262        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23263        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23264        'table',   'ul',         'xmp', 
23265        
23266        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23267       'thead',   'tr', 
23268      
23269       'dir', 'menu', 'ol', 'ul', 'dl',
23270        
23271       'embed',  'object'
23272 ];
23273
23274
23275 Roo.HtmlEditorCore.black = [
23276     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23277         'applet', // 
23278         'base',   'basefont', 'bgsound', 'blink',  'body', 
23279         'frame',  'frameset', 'head',    'html',   'ilayer', 
23280         'iframe', 'layer',  'link',     'meta',    'object',   
23281         'script', 'style' ,'title',  'xml' // clean later..
23282 ];
23283 Roo.HtmlEditorCore.clean = [
23284     'script', 'style', 'title', 'xml'
23285 ];
23286 Roo.HtmlEditorCore.remove = [
23287     'font'
23288 ];
23289 // attributes..
23290
23291 Roo.HtmlEditorCore.ablack = [
23292     'on'
23293 ];
23294     
23295 Roo.HtmlEditorCore.aclean = [ 
23296     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23297 ];
23298
23299 // protocols..
23300 Roo.HtmlEditorCore.pwhite= [
23301         'http',  'https',  'mailto'
23302 ];
23303
23304 // white listed style attributes.
23305 Roo.HtmlEditorCore.cwhite= [
23306       //  'text-align', /// default is to allow most things..
23307       
23308          
23309 //        'font-size'//??
23310 ];
23311
23312 // black listed style attributes.
23313 Roo.HtmlEditorCore.cblack= [
23314       //  'font-size' -- this can be set by the project 
23315 ];
23316
23317
23318 Roo.HtmlEditorCore.swapCodes   =[ 
23319     [    8211, "--" ], 
23320     [    8212, "--" ], 
23321     [    8216,  "'" ],  
23322     [    8217, "'" ],  
23323     [    8220, '"' ],  
23324     [    8221, '"' ],  
23325     [    8226, "*" ],  
23326     [    8230, "..." ]
23327 ]; 
23328
23329     /*
23330  * - LGPL
23331  *
23332  * HtmlEditor
23333  * 
23334  */
23335
23336 /**
23337  * @class Roo.bootstrap.HtmlEditor
23338  * @extends Roo.bootstrap.TextArea
23339  * Bootstrap HtmlEditor class
23340
23341  * @constructor
23342  * Create a new HtmlEditor
23343  * @param {Object} config The config object
23344  */
23345
23346 Roo.bootstrap.HtmlEditor = function(config){
23347     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23348     if (!this.toolbars) {
23349         this.toolbars = [];
23350     }
23351     
23352     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23353     this.addEvents({
23354             /**
23355              * @event initialize
23356              * Fires when the editor is fully initialized (including the iframe)
23357              * @param {HtmlEditor} this
23358              */
23359             initialize: true,
23360             /**
23361              * @event activate
23362              * Fires when the editor is first receives the focus. Any insertion must wait
23363              * until after this event.
23364              * @param {HtmlEditor} this
23365              */
23366             activate: true,
23367              /**
23368              * @event beforesync
23369              * Fires before the textarea is updated with content from the editor iframe. Return false
23370              * to cancel the sync.
23371              * @param {HtmlEditor} this
23372              * @param {String} html
23373              */
23374             beforesync: true,
23375              /**
23376              * @event beforepush
23377              * Fires before the iframe editor is updated with content from the textarea. Return false
23378              * to cancel the push.
23379              * @param {HtmlEditor} this
23380              * @param {String} html
23381              */
23382             beforepush: true,
23383              /**
23384              * @event sync
23385              * Fires when the textarea is updated with content from the editor iframe.
23386              * @param {HtmlEditor} this
23387              * @param {String} html
23388              */
23389             sync: true,
23390              /**
23391              * @event push
23392              * Fires when the iframe editor is updated with content from the textarea.
23393              * @param {HtmlEditor} this
23394              * @param {String} html
23395              */
23396             push: true,
23397              /**
23398              * @event editmodechange
23399              * Fires when the editor switches edit modes
23400              * @param {HtmlEditor} this
23401              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23402              */
23403             editmodechange: true,
23404             /**
23405              * @event editorevent
23406              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23407              * @param {HtmlEditor} this
23408              */
23409             editorevent: true,
23410             /**
23411              * @event firstfocus
23412              * Fires when on first focus - needed by toolbars..
23413              * @param {HtmlEditor} this
23414              */
23415             firstfocus: true,
23416             /**
23417              * @event autosave
23418              * Auto save the htmlEditor value as a file into Events
23419              * @param {HtmlEditor} this
23420              */
23421             autosave: true,
23422             /**
23423              * @event savedpreview
23424              * preview the saved version of htmlEditor
23425              * @param {HtmlEditor} this
23426              */
23427             savedpreview: true
23428         });
23429 };
23430
23431
23432 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23433     
23434     
23435       /**
23436      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23437      */
23438     toolbars : false,
23439     
23440      /**
23441     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23442     */
23443     btns : [],
23444    
23445      /**
23446      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23447      *                        Roo.resizable.
23448      */
23449     resizable : false,
23450      /**
23451      * @cfg {Number} height (in pixels)
23452      */   
23453     height: 300,
23454    /**
23455      * @cfg {Number} width (in pixels)
23456      */   
23457     width: false,
23458     
23459     /**
23460      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23461      * 
23462      */
23463     stylesheets: false,
23464     
23465     // id of frame..
23466     frameId: false,
23467     
23468     // private properties
23469     validationEvent : false,
23470     deferHeight: true,
23471     initialized : false,
23472     activated : false,
23473     
23474     onFocus : Roo.emptyFn,
23475     iframePad:3,
23476     hideMode:'offsets',
23477     
23478     tbContainer : false,
23479     
23480     bodyCls : '',
23481     
23482     toolbarContainer :function() {
23483         return this.wrap.select('.x-html-editor-tb',true).first();
23484     },
23485
23486     /**
23487      * Protected method that will not generally be called directly. It
23488      * is called when the editor creates its toolbar. Override this method if you need to
23489      * add custom toolbar buttons.
23490      * @param {HtmlEditor} editor
23491      */
23492     createToolbar : function(){
23493         Roo.log('renewing');
23494         Roo.log("create toolbars");
23495         
23496         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23497         this.toolbars[0].render(this.toolbarContainer());
23498         
23499         return;
23500         
23501 //        if (!editor.toolbars || !editor.toolbars.length) {
23502 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23503 //        }
23504 //        
23505 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23506 //            editor.toolbars[i] = Roo.factory(
23507 //                    typeof(editor.toolbars[i]) == 'string' ?
23508 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23509 //                Roo.bootstrap.HtmlEditor);
23510 //            editor.toolbars[i].init(editor);
23511 //        }
23512     },
23513
23514      
23515     // private
23516     onRender : function(ct, position)
23517     {
23518        // Roo.log("Call onRender: " + this.xtype);
23519         var _t = this;
23520         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23521       
23522         this.wrap = this.inputEl().wrap({
23523             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23524         });
23525         
23526         this.editorcore.onRender(ct, position);
23527          
23528         if (this.resizable) {
23529             this.resizeEl = new Roo.Resizable(this.wrap, {
23530                 pinned : true,
23531                 wrap: true,
23532                 dynamic : true,
23533                 minHeight : this.height,
23534                 height: this.height,
23535                 handles : this.resizable,
23536                 width: this.width,
23537                 listeners : {
23538                     resize : function(r, w, h) {
23539                         _t.onResize(w,h); // -something
23540                     }
23541                 }
23542             });
23543             
23544         }
23545         this.createToolbar(this);
23546        
23547         
23548         if(!this.width && this.resizable){
23549             this.setSize(this.wrap.getSize());
23550         }
23551         if (this.resizeEl) {
23552             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23553             // should trigger onReize..
23554         }
23555         
23556     },
23557
23558     // private
23559     onResize : function(w, h)
23560     {
23561         Roo.log('resize: ' +w + ',' + h );
23562         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23563         var ew = false;
23564         var eh = false;
23565         
23566         if(this.inputEl() ){
23567             if(typeof w == 'number'){
23568                 var aw = w - this.wrap.getFrameWidth('lr');
23569                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23570                 ew = aw;
23571             }
23572             if(typeof h == 'number'){
23573                  var tbh = -11;  // fixme it needs to tool bar size!
23574                 for (var i =0; i < this.toolbars.length;i++) {
23575                     // fixme - ask toolbars for heights?
23576                     tbh += this.toolbars[i].el.getHeight();
23577                     //if (this.toolbars[i].footer) {
23578                     //    tbh += this.toolbars[i].footer.el.getHeight();
23579                     //}
23580                 }
23581               
23582                 
23583                 
23584                 
23585                 
23586                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23587                 ah -= 5; // knock a few pixes off for look..
23588                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23589                 var eh = ah;
23590             }
23591         }
23592         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23593         this.editorcore.onResize(ew,eh);
23594         
23595     },
23596
23597     /**
23598      * Toggles the editor between standard and source edit mode.
23599      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23600      */
23601     toggleSourceEdit : function(sourceEditMode)
23602     {
23603         this.editorcore.toggleSourceEdit(sourceEditMode);
23604         
23605         if(this.editorcore.sourceEditMode){
23606             Roo.log('editor - showing textarea');
23607             
23608 //            Roo.log('in');
23609 //            Roo.log(this.syncValue());
23610             this.syncValue();
23611             this.inputEl().removeClass(['hide', 'x-hidden']);
23612             this.inputEl().dom.removeAttribute('tabIndex');
23613             this.inputEl().focus();
23614         }else{
23615             Roo.log('editor - hiding textarea');
23616 //            Roo.log('out')
23617 //            Roo.log(this.pushValue()); 
23618             this.pushValue();
23619             
23620             this.inputEl().addClass(['hide', 'x-hidden']);
23621             this.inputEl().dom.setAttribute('tabIndex', -1);
23622             //this.deferFocus();
23623         }
23624          
23625         if(this.resizable){
23626             this.setSize(this.wrap.getSize());
23627         }
23628         
23629         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23630     },
23631  
23632     // private (for BoxComponent)
23633     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23634
23635     // private (for BoxComponent)
23636     getResizeEl : function(){
23637         return this.wrap;
23638     },
23639
23640     // private (for BoxComponent)
23641     getPositionEl : function(){
23642         return this.wrap;
23643     },
23644
23645     // private
23646     initEvents : function(){
23647         this.originalValue = this.getValue();
23648     },
23649
23650 //    /**
23651 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23652 //     * @method
23653 //     */
23654 //    markInvalid : Roo.emptyFn,
23655 //    /**
23656 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23657 //     * @method
23658 //     */
23659 //    clearInvalid : Roo.emptyFn,
23660
23661     setValue : function(v){
23662         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23663         this.editorcore.pushValue();
23664     },
23665
23666      
23667     // private
23668     deferFocus : function(){
23669         this.focus.defer(10, this);
23670     },
23671
23672     // doc'ed in Field
23673     focus : function(){
23674         this.editorcore.focus();
23675         
23676     },
23677       
23678
23679     // private
23680     onDestroy : function(){
23681         
23682         
23683         
23684         if(this.rendered){
23685             
23686             for (var i =0; i < this.toolbars.length;i++) {
23687                 // fixme - ask toolbars for heights?
23688                 this.toolbars[i].onDestroy();
23689             }
23690             
23691             this.wrap.dom.innerHTML = '';
23692             this.wrap.remove();
23693         }
23694     },
23695
23696     // private
23697     onFirstFocus : function(){
23698         //Roo.log("onFirstFocus");
23699         this.editorcore.onFirstFocus();
23700          for (var i =0; i < this.toolbars.length;i++) {
23701             this.toolbars[i].onFirstFocus();
23702         }
23703         
23704     },
23705     
23706     // private
23707     syncValue : function()
23708     {   
23709         this.editorcore.syncValue();
23710     },
23711     
23712     pushValue : function()
23713     {   
23714         this.editorcore.pushValue();
23715     }
23716      
23717     
23718     // hide stuff that is not compatible
23719     /**
23720      * @event blur
23721      * @hide
23722      */
23723     /**
23724      * @event change
23725      * @hide
23726      */
23727     /**
23728      * @event focus
23729      * @hide
23730      */
23731     /**
23732      * @event specialkey
23733      * @hide
23734      */
23735     /**
23736      * @cfg {String} fieldClass @hide
23737      */
23738     /**
23739      * @cfg {String} focusClass @hide
23740      */
23741     /**
23742      * @cfg {String} autoCreate @hide
23743      */
23744     /**
23745      * @cfg {String} inputType @hide
23746      */
23747     /**
23748      * @cfg {String} invalidClass @hide
23749      */
23750     /**
23751      * @cfg {String} invalidText @hide
23752      */
23753     /**
23754      * @cfg {String} msgFx @hide
23755      */
23756     /**
23757      * @cfg {String} validateOnBlur @hide
23758      */
23759 });
23760  
23761     
23762    
23763    
23764    
23765       
23766 Roo.namespace('Roo.bootstrap.htmleditor');
23767 /**
23768  * @class Roo.bootstrap.HtmlEditorToolbar1
23769  * Basic Toolbar
23770  * 
23771  * Usage:
23772  *
23773  new Roo.bootstrap.HtmlEditor({
23774     ....
23775     toolbars : [
23776         new Roo.bootstrap.HtmlEditorToolbar1({
23777             disable : { fonts: 1 , format: 1, ..., ... , ...],
23778             btns : [ .... ]
23779         })
23780     }
23781      
23782  * 
23783  * @cfg {Object} disable List of elements to disable..
23784  * @cfg {Array} btns List of additional buttons.
23785  * 
23786  * 
23787  * NEEDS Extra CSS? 
23788  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23789  */
23790  
23791 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23792 {
23793     
23794     Roo.apply(this, config);
23795     
23796     // default disabled, based on 'good practice'..
23797     this.disable = this.disable || {};
23798     Roo.applyIf(this.disable, {
23799         fontSize : true,
23800         colors : true,
23801         specialElements : true
23802     });
23803     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23804     
23805     this.editor = config.editor;
23806     this.editorcore = config.editor.editorcore;
23807     
23808     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23809     
23810     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23811     // dont call parent... till later.
23812 }
23813 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23814      
23815     bar : true,
23816     
23817     editor : false,
23818     editorcore : false,
23819     
23820     
23821     formats : [
23822         "p" ,  
23823         "h1","h2","h3","h4","h5","h6", 
23824         "pre", "code", 
23825         "abbr", "acronym", "address", "cite", "samp", "var",
23826         'div','span'
23827     ],
23828     
23829     onRender : function(ct, position)
23830     {
23831        // Roo.log("Call onRender: " + this.xtype);
23832         
23833        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23834        Roo.log(this.el);
23835        this.el.dom.style.marginBottom = '0';
23836        var _this = this;
23837        var editorcore = this.editorcore;
23838        var editor= this.editor;
23839        
23840        var children = [];
23841        var btn = function(id,cmd , toggle, handler, html){
23842        
23843             var  event = toggle ? 'toggle' : 'click';
23844        
23845             var a = {
23846                 size : 'sm',
23847                 xtype: 'Button',
23848                 xns: Roo.bootstrap,
23849                 glyphicon : id,
23850                 cmd : id || cmd,
23851                 enableToggle:toggle !== false,
23852                 html : html || '',
23853                 pressed : toggle ? false : null,
23854                 listeners : {}
23855             };
23856             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23857                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23858             };
23859             children.push(a);
23860             return a;
23861        }
23862        
23863     //    var cb_box = function...
23864         
23865         var style = {
23866                 xtype: 'Button',
23867                 size : 'sm',
23868                 xns: Roo.bootstrap,
23869                 glyphicon : 'font',
23870                 //html : 'submit'
23871                 menu : {
23872                     xtype: 'Menu',
23873                     xns: Roo.bootstrap,
23874                     items:  []
23875                 }
23876         };
23877         Roo.each(this.formats, function(f) {
23878             style.menu.items.push({
23879                 xtype :'MenuItem',
23880                 xns: Roo.bootstrap,
23881                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23882                 tagname : f,
23883                 listeners : {
23884                     click : function()
23885                     {
23886                         editorcore.insertTag(this.tagname);
23887                         editor.focus();
23888                     }
23889                 }
23890                 
23891             });
23892         });
23893         children.push(style);   
23894         
23895         btn('bold',false,true);
23896         btn('italic',false,true);
23897         btn('align-left', 'justifyleft',true);
23898         btn('align-center', 'justifycenter',true);
23899         btn('align-right' , 'justifyright',true);
23900         btn('link', false, false, function(btn) {
23901             //Roo.log("create link?");
23902             var url = prompt(this.createLinkText, this.defaultLinkValue);
23903             if(url && url != 'http:/'+'/'){
23904                 this.editorcore.relayCmd('createlink', url);
23905             }
23906         }),
23907         btn('list','insertunorderedlist',true);
23908         btn('pencil', false,true, function(btn){
23909                 Roo.log(this);
23910                 this.toggleSourceEdit(btn.pressed);
23911         });
23912         
23913         if (this.editor.btns.length > 0) {
23914             for (var i = 0; i<this.editor.btns.length; i++) {
23915                 children.push(this.editor.btns[i]);
23916             }
23917         }
23918         
23919         /*
23920         var cog = {
23921                 xtype: 'Button',
23922                 size : 'sm',
23923                 xns: Roo.bootstrap,
23924                 glyphicon : 'cog',
23925                 //html : 'submit'
23926                 menu : {
23927                     xtype: 'Menu',
23928                     xns: Roo.bootstrap,
23929                     items:  []
23930                 }
23931         };
23932         
23933         cog.menu.items.push({
23934             xtype :'MenuItem',
23935             xns: Roo.bootstrap,
23936             html : Clean styles,
23937             tagname : f,
23938             listeners : {
23939                 click : function()
23940                 {
23941                     editorcore.insertTag(this.tagname);
23942                     editor.focus();
23943                 }
23944             }
23945             
23946         });
23947        */
23948         
23949          
23950        this.xtype = 'NavSimplebar';
23951         
23952         for(var i=0;i< children.length;i++) {
23953             
23954             this.buttons.add(this.addxtypeChild(children[i]));
23955             
23956         }
23957         
23958         editor.on('editorevent', this.updateToolbar, this);
23959     },
23960     onBtnClick : function(id)
23961     {
23962        this.editorcore.relayCmd(id);
23963        this.editorcore.focus();
23964     },
23965     
23966     /**
23967      * Protected method that will not generally be called directly. It triggers
23968      * a toolbar update by reading the markup state of the current selection in the editor.
23969      */
23970     updateToolbar: function(){
23971
23972         if(!this.editorcore.activated){
23973             this.editor.onFirstFocus(); // is this neeed?
23974             return;
23975         }
23976
23977         var btns = this.buttons; 
23978         var doc = this.editorcore.doc;
23979         btns.get('bold').setActive(doc.queryCommandState('bold'));
23980         btns.get('italic').setActive(doc.queryCommandState('italic'));
23981         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23982         
23983         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23984         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23985         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23986         
23987         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23988         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23989          /*
23990         
23991         var ans = this.editorcore.getAllAncestors();
23992         if (this.formatCombo) {
23993             
23994             
23995             var store = this.formatCombo.store;
23996             this.formatCombo.setValue("");
23997             for (var i =0; i < ans.length;i++) {
23998                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23999                     // select it..
24000                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24001                     break;
24002                 }
24003             }
24004         }
24005         
24006         
24007         
24008         // hides menus... - so this cant be on a menu...
24009         Roo.bootstrap.MenuMgr.hideAll();
24010         */
24011         Roo.bootstrap.MenuMgr.hideAll();
24012         //this.editorsyncValue();
24013     },
24014     onFirstFocus: function() {
24015         this.buttons.each(function(item){
24016            item.enable();
24017         });
24018     },
24019     toggleSourceEdit : function(sourceEditMode){
24020         
24021           
24022         if(sourceEditMode){
24023             Roo.log("disabling buttons");
24024            this.buttons.each( function(item){
24025                 if(item.cmd != 'pencil'){
24026                     item.disable();
24027                 }
24028             });
24029           
24030         }else{
24031             Roo.log("enabling buttons");
24032             if(this.editorcore.initialized){
24033                 this.buttons.each( function(item){
24034                     item.enable();
24035                 });
24036             }
24037             
24038         }
24039         Roo.log("calling toggole on editor");
24040         // tell the editor that it's been pressed..
24041         this.editor.toggleSourceEdit(sourceEditMode);
24042        
24043     }
24044 });
24045
24046
24047
24048
24049
24050 /**
24051  * @class Roo.bootstrap.Table.AbstractSelectionModel
24052  * @extends Roo.util.Observable
24053  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24054  * implemented by descendant classes.  This class should not be directly instantiated.
24055  * @constructor
24056  */
24057 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24058     this.locked = false;
24059     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24060 };
24061
24062
24063 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24064     /** @ignore Called by the grid automatically. Do not call directly. */
24065     init : function(grid){
24066         this.grid = grid;
24067         this.initEvents();
24068     },
24069
24070     /**
24071      * Locks the selections.
24072      */
24073     lock : function(){
24074         this.locked = true;
24075     },
24076
24077     /**
24078      * Unlocks the selections.
24079      */
24080     unlock : function(){
24081         this.locked = false;
24082     },
24083
24084     /**
24085      * Returns true if the selections are locked.
24086      * @return {Boolean}
24087      */
24088     isLocked : function(){
24089         return this.locked;
24090     }
24091 });
24092 /**
24093  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24094  * @class Roo.bootstrap.Table.RowSelectionModel
24095  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24096  * It supports multiple selections and keyboard selection/navigation. 
24097  * @constructor
24098  * @param {Object} config
24099  */
24100
24101 Roo.bootstrap.Table.RowSelectionModel = function(config){
24102     Roo.apply(this, config);
24103     this.selections = new Roo.util.MixedCollection(false, function(o){
24104         return o.id;
24105     });
24106
24107     this.last = false;
24108     this.lastActive = false;
24109
24110     this.addEvents({
24111         /**
24112              * @event selectionchange
24113              * Fires when the selection changes
24114              * @param {SelectionModel} this
24115              */
24116             "selectionchange" : true,
24117         /**
24118              * @event afterselectionchange
24119              * Fires after the selection changes (eg. by key press or clicking)
24120              * @param {SelectionModel} this
24121              */
24122             "afterselectionchange" : true,
24123         /**
24124              * @event beforerowselect
24125              * Fires when a row is selected being selected, return false to cancel.
24126              * @param {SelectionModel} this
24127              * @param {Number} rowIndex The selected index
24128              * @param {Boolean} keepExisting False if other selections will be cleared
24129              */
24130             "beforerowselect" : true,
24131         /**
24132              * @event rowselect
24133              * Fires when a row is selected.
24134              * @param {SelectionModel} this
24135              * @param {Number} rowIndex The selected index
24136              * @param {Roo.data.Record} r The record
24137              */
24138             "rowselect" : true,
24139         /**
24140              * @event rowdeselect
24141              * Fires when a row is deselected.
24142              * @param {SelectionModel} this
24143              * @param {Number} rowIndex The selected index
24144              */
24145         "rowdeselect" : true
24146     });
24147     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24148     this.locked = false;
24149  };
24150
24151 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24152     /**
24153      * @cfg {Boolean} singleSelect
24154      * True to allow selection of only one row at a time (defaults to false)
24155      */
24156     singleSelect : false,
24157
24158     // private
24159     initEvents : function()
24160     {
24161
24162         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24163         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24164         //}else{ // allow click to work like normal
24165          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24166         //}
24167         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24168         this.grid.on("rowclick", this.handleMouseDown, this);
24169         
24170         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24171             "up" : function(e){
24172                 if(!e.shiftKey){
24173                     this.selectPrevious(e.shiftKey);
24174                 }else if(this.last !== false && this.lastActive !== false){
24175                     var last = this.last;
24176                     this.selectRange(this.last,  this.lastActive-1);
24177                     this.grid.getView().focusRow(this.lastActive);
24178                     if(last !== false){
24179                         this.last = last;
24180                     }
24181                 }else{
24182                     this.selectFirstRow();
24183                 }
24184                 this.fireEvent("afterselectionchange", this);
24185             },
24186             "down" : function(e){
24187                 if(!e.shiftKey){
24188                     this.selectNext(e.shiftKey);
24189                 }else if(this.last !== false && this.lastActive !== false){
24190                     var last = this.last;
24191                     this.selectRange(this.last,  this.lastActive+1);
24192                     this.grid.getView().focusRow(this.lastActive);
24193                     if(last !== false){
24194                         this.last = last;
24195                     }
24196                 }else{
24197                     this.selectFirstRow();
24198                 }
24199                 this.fireEvent("afterselectionchange", this);
24200             },
24201             scope: this
24202         });
24203         this.grid.store.on('load', function(){
24204             this.selections.clear();
24205         },this);
24206         /*
24207         var view = this.grid.view;
24208         view.on("refresh", this.onRefresh, this);
24209         view.on("rowupdated", this.onRowUpdated, this);
24210         view.on("rowremoved", this.onRemove, this);
24211         */
24212     },
24213
24214     // private
24215     onRefresh : function()
24216     {
24217         var ds = this.grid.store, i, v = this.grid.view;
24218         var s = this.selections;
24219         s.each(function(r){
24220             if((i = ds.indexOfId(r.id)) != -1){
24221                 v.onRowSelect(i);
24222             }else{
24223                 s.remove(r);
24224             }
24225         });
24226     },
24227
24228     // private
24229     onRemove : function(v, index, r){
24230         this.selections.remove(r);
24231     },
24232
24233     // private
24234     onRowUpdated : function(v, index, r){
24235         if(this.isSelected(r)){
24236             v.onRowSelect(index);
24237         }
24238     },
24239
24240     /**
24241      * Select records.
24242      * @param {Array} records The records to select
24243      * @param {Boolean} keepExisting (optional) True to keep existing selections
24244      */
24245     selectRecords : function(records, keepExisting)
24246     {
24247         if(!keepExisting){
24248             this.clearSelections();
24249         }
24250             var ds = this.grid.store;
24251         for(var i = 0, len = records.length; i < len; i++){
24252             this.selectRow(ds.indexOf(records[i]), true);
24253         }
24254     },
24255
24256     /**
24257      * Gets the number of selected rows.
24258      * @return {Number}
24259      */
24260     getCount : function(){
24261         return this.selections.length;
24262     },
24263
24264     /**
24265      * Selects the first row in the grid.
24266      */
24267     selectFirstRow : function(){
24268         this.selectRow(0);
24269     },
24270
24271     /**
24272      * Select the last row.
24273      * @param {Boolean} keepExisting (optional) True to keep existing selections
24274      */
24275     selectLastRow : function(keepExisting){
24276         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24277         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24278     },
24279
24280     /**
24281      * Selects the row immediately following the last selected row.
24282      * @param {Boolean} keepExisting (optional) True to keep existing selections
24283      */
24284     selectNext : function(keepExisting)
24285     {
24286             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24287             this.selectRow(this.last+1, keepExisting);
24288             this.grid.getView().focusRow(this.last);
24289         }
24290     },
24291
24292     /**
24293      * Selects the row that precedes the last selected row.
24294      * @param {Boolean} keepExisting (optional) True to keep existing selections
24295      */
24296     selectPrevious : function(keepExisting){
24297         if(this.last){
24298             this.selectRow(this.last-1, keepExisting);
24299             this.grid.getView().focusRow(this.last);
24300         }
24301     },
24302
24303     /**
24304      * Returns the selected records
24305      * @return {Array} Array of selected records
24306      */
24307     getSelections : function(){
24308         return [].concat(this.selections.items);
24309     },
24310
24311     /**
24312      * Returns the first selected record.
24313      * @return {Record}
24314      */
24315     getSelected : function(){
24316         return this.selections.itemAt(0);
24317     },
24318
24319
24320     /**
24321      * Clears all selections.
24322      */
24323     clearSelections : function(fast)
24324     {
24325         if(this.locked) {
24326             return;
24327         }
24328         if(fast !== true){
24329                 var ds = this.grid.store;
24330             var s = this.selections;
24331             s.each(function(r){
24332                 this.deselectRow(ds.indexOfId(r.id));
24333             }, this);
24334             s.clear();
24335         }else{
24336             this.selections.clear();
24337         }
24338         this.last = false;
24339     },
24340
24341
24342     /**
24343      * Selects all rows.
24344      */
24345     selectAll : function(){
24346         if(this.locked) {
24347             return;
24348         }
24349         this.selections.clear();
24350         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24351             this.selectRow(i, true);
24352         }
24353     },
24354
24355     /**
24356      * Returns True if there is a selection.
24357      * @return {Boolean}
24358      */
24359     hasSelection : function(){
24360         return this.selections.length > 0;
24361     },
24362
24363     /**
24364      * Returns True if the specified row is selected.
24365      * @param {Number/Record} record The record or index of the record to check
24366      * @return {Boolean}
24367      */
24368     isSelected : function(index){
24369             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24370         return (r && this.selections.key(r.id) ? true : false);
24371     },
24372
24373     /**
24374      * Returns True if the specified record id is selected.
24375      * @param {String} id The id of record to check
24376      * @return {Boolean}
24377      */
24378     isIdSelected : function(id){
24379         return (this.selections.key(id) ? true : false);
24380     },
24381
24382
24383     // private
24384     handleMouseDBClick : function(e, t){
24385         
24386     },
24387     // private
24388     handleMouseDown : function(e, t)
24389     {
24390             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24391         if(this.isLocked() || rowIndex < 0 ){
24392             return;
24393         };
24394         if(e.shiftKey && this.last !== false){
24395             var last = this.last;
24396             this.selectRange(last, rowIndex, e.ctrlKey);
24397             this.last = last; // reset the last
24398             t.focus();
24399     
24400         }else{
24401             var isSelected = this.isSelected(rowIndex);
24402             //Roo.log("select row:" + rowIndex);
24403             if(isSelected){
24404                 this.deselectRow(rowIndex);
24405             } else {
24406                         this.selectRow(rowIndex, true);
24407             }
24408     
24409             /*
24410                 if(e.button !== 0 && isSelected){
24411                 alert('rowIndex 2: ' + rowIndex);
24412                     view.focusRow(rowIndex);
24413                 }else if(e.ctrlKey && isSelected){
24414                     this.deselectRow(rowIndex);
24415                 }else if(!isSelected){
24416                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24417                     view.focusRow(rowIndex);
24418                 }
24419             */
24420         }
24421         this.fireEvent("afterselectionchange", this);
24422     },
24423     // private
24424     handleDragableRowClick :  function(grid, rowIndex, e) 
24425     {
24426         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24427             this.selectRow(rowIndex, false);
24428             grid.view.focusRow(rowIndex);
24429              this.fireEvent("afterselectionchange", this);
24430         }
24431     },
24432     
24433     /**
24434      * Selects multiple rows.
24435      * @param {Array} rows Array of the indexes of the row to select
24436      * @param {Boolean} keepExisting (optional) True to keep existing selections
24437      */
24438     selectRows : function(rows, keepExisting){
24439         if(!keepExisting){
24440             this.clearSelections();
24441         }
24442         for(var i = 0, len = rows.length; i < len; i++){
24443             this.selectRow(rows[i], true);
24444         }
24445     },
24446
24447     /**
24448      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24449      * @param {Number} startRow The index of the first row in the range
24450      * @param {Number} endRow The index of the last row in the range
24451      * @param {Boolean} keepExisting (optional) True to retain existing selections
24452      */
24453     selectRange : function(startRow, endRow, keepExisting){
24454         if(this.locked) {
24455             return;
24456         }
24457         if(!keepExisting){
24458             this.clearSelections();
24459         }
24460         if(startRow <= endRow){
24461             for(var i = startRow; i <= endRow; i++){
24462                 this.selectRow(i, true);
24463             }
24464         }else{
24465             for(var i = startRow; i >= endRow; i--){
24466                 this.selectRow(i, true);
24467             }
24468         }
24469     },
24470
24471     /**
24472      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24473      * @param {Number} startRow The index of the first row in the range
24474      * @param {Number} endRow The index of the last row in the range
24475      */
24476     deselectRange : function(startRow, endRow, preventViewNotify){
24477         if(this.locked) {
24478             return;
24479         }
24480         for(var i = startRow; i <= endRow; i++){
24481             this.deselectRow(i, preventViewNotify);
24482         }
24483     },
24484
24485     /**
24486      * Selects a row.
24487      * @param {Number} row The index of the row to select
24488      * @param {Boolean} keepExisting (optional) True to keep existing selections
24489      */
24490     selectRow : function(index, keepExisting, preventViewNotify)
24491     {
24492             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24493             return;
24494         }
24495         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24496             if(!keepExisting || this.singleSelect){
24497                 this.clearSelections();
24498             }
24499             
24500             var r = this.grid.store.getAt(index);
24501             //console.log('selectRow - record id :' + r.id);
24502             
24503             this.selections.add(r);
24504             this.last = this.lastActive = index;
24505             if(!preventViewNotify){
24506                 var proxy = new Roo.Element(
24507                                 this.grid.getRowDom(index)
24508                 );
24509                 proxy.addClass('bg-info info');
24510             }
24511             this.fireEvent("rowselect", this, index, r);
24512             this.fireEvent("selectionchange", this);
24513         }
24514     },
24515
24516     /**
24517      * Deselects a row.
24518      * @param {Number} row The index of the row to deselect
24519      */
24520     deselectRow : function(index, preventViewNotify)
24521     {
24522         if(this.locked) {
24523             return;
24524         }
24525         if(this.last == index){
24526             this.last = false;
24527         }
24528         if(this.lastActive == index){
24529             this.lastActive = false;
24530         }
24531         
24532         var r = this.grid.store.getAt(index);
24533         if (!r) {
24534             return;
24535         }
24536         
24537         this.selections.remove(r);
24538         //.console.log('deselectRow - record id :' + r.id);
24539         if(!preventViewNotify){
24540         
24541             var proxy = new Roo.Element(
24542                 this.grid.getRowDom(index)
24543             );
24544             proxy.removeClass('bg-info info');
24545         }
24546         this.fireEvent("rowdeselect", this, index);
24547         this.fireEvent("selectionchange", this);
24548     },
24549
24550     // private
24551     restoreLast : function(){
24552         if(this._last){
24553             this.last = this._last;
24554         }
24555     },
24556
24557     // private
24558     acceptsNav : function(row, col, cm){
24559         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24560     },
24561
24562     // private
24563     onEditorKey : function(field, e){
24564         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24565         if(k == e.TAB){
24566             e.stopEvent();
24567             ed.completeEdit();
24568             if(e.shiftKey){
24569                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24570             }else{
24571                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24572             }
24573         }else if(k == e.ENTER && !e.ctrlKey){
24574             e.stopEvent();
24575             ed.completeEdit();
24576             if(e.shiftKey){
24577                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24578             }else{
24579                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24580             }
24581         }else if(k == e.ESC){
24582             ed.cancelEdit();
24583         }
24584         if(newCell){
24585             g.startEditing(newCell[0], newCell[1]);
24586         }
24587     }
24588 });
24589 /*
24590  * Based on:
24591  * Ext JS Library 1.1.1
24592  * Copyright(c) 2006-2007, Ext JS, LLC.
24593  *
24594  * Originally Released Under LGPL - original licence link has changed is not relivant.
24595  *
24596  * Fork - LGPL
24597  * <script type="text/javascript">
24598  */
24599  
24600 /**
24601  * @class Roo.bootstrap.PagingToolbar
24602  * @extends Roo.bootstrap.NavSimplebar
24603  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24604  * @constructor
24605  * Create a new PagingToolbar
24606  * @param {Object} config The config object
24607  * @param {Roo.data.Store} store
24608  */
24609 Roo.bootstrap.PagingToolbar = function(config)
24610 {
24611     // old args format still supported... - xtype is prefered..
24612         // created from xtype...
24613     
24614     this.ds = config.dataSource;
24615     
24616     if (config.store && !this.ds) {
24617         this.store= Roo.factory(config.store, Roo.data);
24618         this.ds = this.store;
24619         this.ds.xmodule = this.xmodule || false;
24620     }
24621     
24622     this.toolbarItems = [];
24623     if (config.items) {
24624         this.toolbarItems = config.items;
24625     }
24626     
24627     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24628     
24629     this.cursor = 0;
24630     
24631     if (this.ds) { 
24632         this.bind(this.ds);
24633     }
24634     
24635     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24636     
24637 };
24638
24639 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24640     /**
24641      * @cfg {Roo.data.Store} dataSource
24642      * The underlying data store providing the paged data
24643      */
24644     /**
24645      * @cfg {String/HTMLElement/Element} container
24646      * container The id or element that will contain the toolbar
24647      */
24648     /**
24649      * @cfg {Boolean} displayInfo
24650      * True to display the displayMsg (defaults to false)
24651      */
24652     /**
24653      * @cfg {Number} pageSize
24654      * The number of records to display per page (defaults to 20)
24655      */
24656     pageSize: 20,
24657     /**
24658      * @cfg {String} displayMsg
24659      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24660      */
24661     displayMsg : 'Displaying {0} - {1} of {2}',
24662     /**
24663      * @cfg {String} emptyMsg
24664      * The message to display when no records are found (defaults to "No data to display")
24665      */
24666     emptyMsg : 'No data to display',
24667     /**
24668      * Customizable piece of the default paging text (defaults to "Page")
24669      * @type String
24670      */
24671     beforePageText : "Page",
24672     /**
24673      * Customizable piece of the default paging text (defaults to "of %0")
24674      * @type String
24675      */
24676     afterPageText : "of {0}",
24677     /**
24678      * Customizable piece of the default paging text (defaults to "First Page")
24679      * @type String
24680      */
24681     firstText : "First Page",
24682     /**
24683      * Customizable piece of the default paging text (defaults to "Previous Page")
24684      * @type String
24685      */
24686     prevText : "Previous Page",
24687     /**
24688      * Customizable piece of the default paging text (defaults to "Next Page")
24689      * @type String
24690      */
24691     nextText : "Next Page",
24692     /**
24693      * Customizable piece of the default paging text (defaults to "Last Page")
24694      * @type String
24695      */
24696     lastText : "Last Page",
24697     /**
24698      * Customizable piece of the default paging text (defaults to "Refresh")
24699      * @type String
24700      */
24701     refreshText : "Refresh",
24702
24703     buttons : false,
24704     // private
24705     onRender : function(ct, position) 
24706     {
24707         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24708         this.navgroup.parentId = this.id;
24709         this.navgroup.onRender(this.el, null);
24710         // add the buttons to the navgroup
24711         
24712         if(this.displayInfo){
24713             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24714             this.displayEl = this.el.select('.x-paging-info', true).first();
24715 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24716 //            this.displayEl = navel.el.select('span',true).first();
24717         }
24718         
24719         var _this = this;
24720         
24721         if(this.buttons){
24722             Roo.each(_this.buttons, function(e){ // this might need to use render????
24723                Roo.factory(e).render(_this.el);
24724             });
24725         }
24726             
24727         Roo.each(_this.toolbarItems, function(e) {
24728             _this.navgroup.addItem(e);
24729         });
24730         
24731         
24732         this.first = this.navgroup.addItem({
24733             tooltip: this.firstText,
24734             cls: "prev",
24735             icon : 'fa fa-step-backward',
24736             disabled: true,
24737             preventDefault: true,
24738             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24739         });
24740         
24741         this.prev =  this.navgroup.addItem({
24742             tooltip: this.prevText,
24743             cls: "prev",
24744             icon : 'fa fa-backward',
24745             disabled: true,
24746             preventDefault: true,
24747             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24748         });
24749     //this.addSeparator();
24750         
24751         
24752         var field = this.navgroup.addItem( {
24753             tagtype : 'span',
24754             cls : 'x-paging-position',
24755             
24756             html : this.beforePageText  +
24757                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24758                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24759          } ); //?? escaped?
24760         
24761         this.field = field.el.select('input', true).first();
24762         this.field.on("keydown", this.onPagingKeydown, this);
24763         this.field.on("focus", function(){this.dom.select();});
24764     
24765     
24766         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24767         //this.field.setHeight(18);
24768         //this.addSeparator();
24769         this.next = this.navgroup.addItem({
24770             tooltip: this.nextText,
24771             cls: "next",
24772             html : ' <i class="fa fa-forward">',
24773             disabled: true,
24774             preventDefault: true,
24775             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24776         });
24777         this.last = this.navgroup.addItem({
24778             tooltip: this.lastText,
24779             icon : 'fa fa-step-forward',
24780             cls: "next",
24781             disabled: true,
24782             preventDefault: true,
24783             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24784         });
24785     //this.addSeparator();
24786         this.loading = this.navgroup.addItem({
24787             tooltip: this.refreshText,
24788             icon: 'fa fa-refresh',
24789             preventDefault: true,
24790             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24791         });
24792         
24793     },
24794
24795     // private
24796     updateInfo : function(){
24797         if(this.displayEl){
24798             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24799             var msg = count == 0 ?
24800                 this.emptyMsg :
24801                 String.format(
24802                     this.displayMsg,
24803                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24804                 );
24805             this.displayEl.update(msg);
24806         }
24807     },
24808
24809     // private
24810     onLoad : function(ds, r, o)
24811     {
24812         this.cursor = o.params.start ? o.params.start : 0;
24813         
24814         var d = this.getPageData(),
24815             ap = d.activePage,
24816             ps = d.pages;
24817         
24818         
24819         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24820         this.field.dom.value = ap;
24821         this.first.setDisabled(ap == 1);
24822         this.prev.setDisabled(ap == 1);
24823         this.next.setDisabled(ap == ps);
24824         this.last.setDisabled(ap == ps);
24825         this.loading.enable();
24826         this.updateInfo();
24827     },
24828
24829     // private
24830     getPageData : function(){
24831         var total = this.ds.getTotalCount();
24832         return {
24833             total : total,
24834             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24835             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24836         };
24837     },
24838
24839     // private
24840     onLoadError : function(){
24841         this.loading.enable();
24842     },
24843
24844     // private
24845     onPagingKeydown : function(e){
24846         var k = e.getKey();
24847         var d = this.getPageData();
24848         if(k == e.RETURN){
24849             var v = this.field.dom.value, pageNum;
24850             if(!v || isNaN(pageNum = parseInt(v, 10))){
24851                 this.field.dom.value = d.activePage;
24852                 return;
24853             }
24854             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24855             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24856             e.stopEvent();
24857         }
24858         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))
24859         {
24860           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24861           this.field.dom.value = pageNum;
24862           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24863           e.stopEvent();
24864         }
24865         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24866         {
24867           var v = this.field.dom.value, pageNum; 
24868           var increment = (e.shiftKey) ? 10 : 1;
24869           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24870                 increment *= -1;
24871           }
24872           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24873             this.field.dom.value = d.activePage;
24874             return;
24875           }
24876           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24877           {
24878             this.field.dom.value = parseInt(v, 10) + increment;
24879             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24880             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24881           }
24882           e.stopEvent();
24883         }
24884     },
24885
24886     // private
24887     beforeLoad : function(){
24888         if(this.loading){
24889             this.loading.disable();
24890         }
24891     },
24892
24893     // private
24894     onClick : function(which){
24895         
24896         var ds = this.ds;
24897         if (!ds) {
24898             return;
24899         }
24900         
24901         switch(which){
24902             case "first":
24903                 ds.load({params:{start: 0, limit: this.pageSize}});
24904             break;
24905             case "prev":
24906                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24907             break;
24908             case "next":
24909                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24910             break;
24911             case "last":
24912                 var total = ds.getTotalCount();
24913                 var extra = total % this.pageSize;
24914                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24915                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24916             break;
24917             case "refresh":
24918                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24919             break;
24920         }
24921     },
24922
24923     /**
24924      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24925      * @param {Roo.data.Store} store The data store to unbind
24926      */
24927     unbind : function(ds){
24928         ds.un("beforeload", this.beforeLoad, this);
24929         ds.un("load", this.onLoad, this);
24930         ds.un("loadexception", this.onLoadError, this);
24931         ds.un("remove", this.updateInfo, this);
24932         ds.un("add", this.updateInfo, this);
24933         this.ds = undefined;
24934     },
24935
24936     /**
24937      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24938      * @param {Roo.data.Store} store The data store to bind
24939      */
24940     bind : function(ds){
24941         ds.on("beforeload", this.beforeLoad, this);
24942         ds.on("load", this.onLoad, this);
24943         ds.on("loadexception", this.onLoadError, this);
24944         ds.on("remove", this.updateInfo, this);
24945         ds.on("add", this.updateInfo, this);
24946         this.ds = ds;
24947     }
24948 });/*
24949  * - LGPL
24950  *
24951  * element
24952  * 
24953  */
24954
24955 /**
24956  * @class Roo.bootstrap.MessageBar
24957  * @extends Roo.bootstrap.Component
24958  * Bootstrap MessageBar class
24959  * @cfg {String} html contents of the MessageBar
24960  * @cfg {String} weight (info | success | warning | danger) default info
24961  * @cfg {String} beforeClass insert the bar before the given class
24962  * @cfg {Boolean} closable (true | false) default false
24963  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24964  * 
24965  * @constructor
24966  * Create a new Element
24967  * @param {Object} config The config object
24968  */
24969
24970 Roo.bootstrap.MessageBar = function(config){
24971     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24972 };
24973
24974 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24975     
24976     html: '',
24977     weight: 'info',
24978     closable: false,
24979     fixed: false,
24980     beforeClass: 'bootstrap-sticky-wrap',
24981     
24982     getAutoCreate : function(){
24983         
24984         var cfg = {
24985             tag: 'div',
24986             cls: 'alert alert-dismissable alert-' + this.weight,
24987             cn: [
24988                 {
24989                     tag: 'span',
24990                     cls: 'message',
24991                     html: this.html || ''
24992                 }
24993             ]
24994         };
24995         
24996         if(this.fixed){
24997             cfg.cls += ' alert-messages-fixed';
24998         }
24999         
25000         if(this.closable){
25001             cfg.cn.push({
25002                 tag: 'button',
25003                 cls: 'close',
25004                 html: 'x'
25005             });
25006         }
25007         
25008         return cfg;
25009     },
25010     
25011     onRender : function(ct, position)
25012     {
25013         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25014         
25015         if(!this.el){
25016             var cfg = Roo.apply({},  this.getAutoCreate());
25017             cfg.id = Roo.id();
25018             
25019             if (this.cls) {
25020                 cfg.cls += ' ' + this.cls;
25021             }
25022             if (this.style) {
25023                 cfg.style = this.style;
25024             }
25025             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25026             
25027             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25028         }
25029         
25030         this.el.select('>button.close').on('click', this.hide, this);
25031         
25032     },
25033     
25034     show : function()
25035     {
25036         if (!this.rendered) {
25037             this.render();
25038         }
25039         
25040         this.el.show();
25041         
25042         this.fireEvent('show', this);
25043         
25044     },
25045     
25046     hide : function()
25047     {
25048         if (!this.rendered) {
25049             this.render();
25050         }
25051         
25052         this.el.hide();
25053         
25054         this.fireEvent('hide', this);
25055     },
25056     
25057     update : function()
25058     {
25059 //        var e = this.el.dom.firstChild;
25060 //        
25061 //        if(this.closable){
25062 //            e = e.nextSibling;
25063 //        }
25064 //        
25065 //        e.data = this.html || '';
25066
25067         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25068     }
25069    
25070 });
25071
25072  
25073
25074      /*
25075  * - LGPL
25076  *
25077  * Graph
25078  * 
25079  */
25080
25081
25082 /**
25083  * @class Roo.bootstrap.Graph
25084  * @extends Roo.bootstrap.Component
25085  * Bootstrap Graph class
25086 > Prameters
25087  -sm {number} sm 4
25088  -md {number} md 5
25089  @cfg {String} graphtype  bar | vbar | pie
25090  @cfg {number} g_x coodinator | centre x (pie)
25091  @cfg {number} g_y coodinator | centre y (pie)
25092  @cfg {number} g_r radius (pie)
25093  @cfg {number} g_height height of the chart (respected by all elements in the set)
25094  @cfg {number} g_width width of the chart (respected by all elements in the set)
25095  @cfg {Object} title The title of the chart
25096     
25097  -{Array}  values
25098  -opts (object) options for the chart 
25099      o {
25100      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25101      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25102      o vgutter (number)
25103      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.
25104      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25105      o to
25106      o stretch (boolean)
25107      o }
25108  -opts (object) options for the pie
25109      o{
25110      o cut
25111      o startAngle (number)
25112      o endAngle (number)
25113      } 
25114  *
25115  * @constructor
25116  * Create a new Input
25117  * @param {Object} config The config object
25118  */
25119
25120 Roo.bootstrap.Graph = function(config){
25121     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25122     
25123     this.addEvents({
25124         // img events
25125         /**
25126          * @event click
25127          * The img click event for the img.
25128          * @param {Roo.EventObject} e
25129          */
25130         "click" : true
25131     });
25132 };
25133
25134 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25135     
25136     sm: 4,
25137     md: 5,
25138     graphtype: 'bar',
25139     g_height: 250,
25140     g_width: 400,
25141     g_x: 50,
25142     g_y: 50,
25143     g_r: 30,
25144     opts:{
25145         //g_colors: this.colors,
25146         g_type: 'soft',
25147         g_gutter: '20%'
25148
25149     },
25150     title : false,
25151
25152     getAutoCreate : function(){
25153         
25154         var cfg = {
25155             tag: 'div',
25156             html : null
25157         };
25158         
25159         
25160         return  cfg;
25161     },
25162
25163     onRender : function(ct,position){
25164         
25165         
25166         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25167         
25168         if (typeof(Raphael) == 'undefined') {
25169             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25170             return;
25171         }
25172         
25173         this.raphael = Raphael(this.el.dom);
25174         
25175                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25176                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25177                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25178                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25179                 /*
25180                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25181                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25182                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25183                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25184                 
25185                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25186                 r.barchart(330, 10, 300, 220, data1);
25187                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25188                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25189                 */
25190                 
25191                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25192                 // r.barchart(30, 30, 560, 250,  xdata, {
25193                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25194                 //     axis : "0 0 1 1",
25195                 //     axisxlabels :  xdata
25196                 //     //yvalues : cols,
25197                    
25198                 // });
25199 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25200 //        
25201 //        this.load(null,xdata,{
25202 //                axis : "0 0 1 1",
25203 //                axisxlabels :  xdata
25204 //                });
25205
25206     },
25207
25208     load : function(graphtype,xdata,opts)
25209     {
25210         this.raphael.clear();
25211         if(!graphtype) {
25212             graphtype = this.graphtype;
25213         }
25214         if(!opts){
25215             opts = this.opts;
25216         }
25217         var r = this.raphael,
25218             fin = function () {
25219                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25220             },
25221             fout = function () {
25222                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25223             },
25224             pfin = function() {
25225                 this.sector.stop();
25226                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25227
25228                 if (this.label) {
25229                     this.label[0].stop();
25230                     this.label[0].attr({ r: 7.5 });
25231                     this.label[1].attr({ "font-weight": 800 });
25232                 }
25233             },
25234             pfout = function() {
25235                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25236
25237                 if (this.label) {
25238                     this.label[0].animate({ r: 5 }, 500, "bounce");
25239                     this.label[1].attr({ "font-weight": 400 });
25240                 }
25241             };
25242
25243         switch(graphtype){
25244             case 'bar':
25245                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25246                 break;
25247             case 'hbar':
25248                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25249                 break;
25250             case 'pie':
25251 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25252 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25253 //            
25254                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25255                 
25256                 break;
25257
25258         }
25259         
25260         if(this.title){
25261             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25262         }
25263         
25264     },
25265     
25266     setTitle: function(o)
25267     {
25268         this.title = o;
25269     },
25270     
25271     initEvents: function() {
25272         
25273         if(!this.href){
25274             this.el.on('click', this.onClick, this);
25275         }
25276     },
25277     
25278     onClick : function(e)
25279     {
25280         Roo.log('img onclick');
25281         this.fireEvent('click', this, e);
25282     }
25283    
25284 });
25285
25286  
25287 /*
25288  * - LGPL
25289  *
25290  * numberBox
25291  * 
25292  */
25293 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25294
25295 /**
25296  * @class Roo.bootstrap.dash.NumberBox
25297  * @extends Roo.bootstrap.Component
25298  * Bootstrap NumberBox class
25299  * @cfg {String} headline Box headline
25300  * @cfg {String} content Box content
25301  * @cfg {String} icon Box icon
25302  * @cfg {String} footer Footer text
25303  * @cfg {String} fhref Footer href
25304  * 
25305  * @constructor
25306  * Create a new NumberBox
25307  * @param {Object} config The config object
25308  */
25309
25310
25311 Roo.bootstrap.dash.NumberBox = function(config){
25312     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25313     
25314 };
25315
25316 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25317     
25318     headline : '',
25319     content : '',
25320     icon : '',
25321     footer : '',
25322     fhref : '',
25323     ficon : '',
25324     
25325     getAutoCreate : function(){
25326         
25327         var cfg = {
25328             tag : 'div',
25329             cls : 'small-box ',
25330             cn : [
25331                 {
25332                     tag : 'div',
25333                     cls : 'inner',
25334                     cn :[
25335                         {
25336                             tag : 'h3',
25337                             cls : 'roo-headline',
25338                             html : this.headline
25339                         },
25340                         {
25341                             tag : 'p',
25342                             cls : 'roo-content',
25343                             html : this.content
25344                         }
25345                     ]
25346                 }
25347             ]
25348         };
25349         
25350         if(this.icon){
25351             cfg.cn.push({
25352                 tag : 'div',
25353                 cls : 'icon',
25354                 cn :[
25355                     {
25356                         tag : 'i',
25357                         cls : 'ion ' + this.icon
25358                     }
25359                 ]
25360             });
25361         }
25362         
25363         if(this.footer){
25364             var footer = {
25365                 tag : 'a',
25366                 cls : 'small-box-footer',
25367                 href : this.fhref || '#',
25368                 html : this.footer
25369             };
25370             
25371             cfg.cn.push(footer);
25372             
25373         }
25374         
25375         return  cfg;
25376     },
25377
25378     onRender : function(ct,position){
25379         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25380
25381
25382        
25383                 
25384     },
25385
25386     setHeadline: function (value)
25387     {
25388         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25389     },
25390     
25391     setFooter: function (value, href)
25392     {
25393         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25394         
25395         if(href){
25396             this.el.select('a.small-box-footer',true).first().attr('href', href);
25397         }
25398         
25399     },
25400
25401     setContent: function (value)
25402     {
25403         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25404     },
25405
25406     initEvents: function() 
25407     {   
25408         
25409     }
25410     
25411 });
25412
25413  
25414 /*
25415  * - LGPL
25416  *
25417  * TabBox
25418  * 
25419  */
25420 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25421
25422 /**
25423  * @class Roo.bootstrap.dash.TabBox
25424  * @extends Roo.bootstrap.Component
25425  * Bootstrap TabBox class
25426  * @cfg {String} title Title of the TabBox
25427  * @cfg {String} icon Icon of the TabBox
25428  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25429  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25430  * 
25431  * @constructor
25432  * Create a new TabBox
25433  * @param {Object} config The config object
25434  */
25435
25436
25437 Roo.bootstrap.dash.TabBox = function(config){
25438     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25439     this.addEvents({
25440         // raw events
25441         /**
25442          * @event addpane
25443          * When a pane is added
25444          * @param {Roo.bootstrap.dash.TabPane} pane
25445          */
25446         "addpane" : true,
25447         /**
25448          * @event activatepane
25449          * When a pane is activated
25450          * @param {Roo.bootstrap.dash.TabPane} pane
25451          */
25452         "activatepane" : true
25453         
25454          
25455     });
25456     
25457     this.panes = [];
25458 };
25459
25460 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25461
25462     title : '',
25463     icon : false,
25464     showtabs : true,
25465     tabScrollable : false,
25466     
25467     getChildContainer : function()
25468     {
25469         return this.el.select('.tab-content', true).first();
25470     },
25471     
25472     getAutoCreate : function(){
25473         
25474         var header = {
25475             tag: 'li',
25476             cls: 'pull-left header',
25477             html: this.title,
25478             cn : []
25479         };
25480         
25481         if(this.icon){
25482             header.cn.push({
25483                 tag: 'i',
25484                 cls: 'fa ' + this.icon
25485             });
25486         }
25487         
25488         var h = {
25489             tag: 'ul',
25490             cls: 'nav nav-tabs pull-right',
25491             cn: [
25492                 header
25493             ]
25494         };
25495         
25496         if(this.tabScrollable){
25497             h = {
25498                 tag: 'div',
25499                 cls: 'tab-header',
25500                 cn: [
25501                     {
25502                         tag: 'ul',
25503                         cls: 'nav nav-tabs pull-right',
25504                         cn: [
25505                             header
25506                         ]
25507                     }
25508                 ]
25509             };
25510         }
25511         
25512         var cfg = {
25513             tag: 'div',
25514             cls: 'nav-tabs-custom',
25515             cn: [
25516                 h,
25517                 {
25518                     tag: 'div',
25519                     cls: 'tab-content no-padding',
25520                     cn: []
25521                 }
25522             ]
25523         };
25524
25525         return  cfg;
25526     },
25527     initEvents : function()
25528     {
25529         //Roo.log('add add pane handler');
25530         this.on('addpane', this.onAddPane, this);
25531     },
25532      /**
25533      * Updates the box title
25534      * @param {String} html to set the title to.
25535      */
25536     setTitle : function(value)
25537     {
25538         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25539     },
25540     onAddPane : function(pane)
25541     {
25542         this.panes.push(pane);
25543         //Roo.log('addpane');
25544         //Roo.log(pane);
25545         // tabs are rendere left to right..
25546         if(!this.showtabs){
25547             return;
25548         }
25549         
25550         var ctr = this.el.select('.nav-tabs', true).first();
25551          
25552          
25553         var existing = ctr.select('.nav-tab',true);
25554         var qty = existing.getCount();;
25555         
25556         
25557         var tab = ctr.createChild({
25558             tag : 'li',
25559             cls : 'nav-tab' + (qty ? '' : ' active'),
25560             cn : [
25561                 {
25562                     tag : 'a',
25563                     href:'#',
25564                     html : pane.title
25565                 }
25566             ]
25567         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25568         pane.tab = tab;
25569         
25570         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25571         if (!qty) {
25572             pane.el.addClass('active');
25573         }
25574         
25575                 
25576     },
25577     onTabClick : function(ev,un,ob,pane)
25578     {
25579         //Roo.log('tab - prev default');
25580         ev.preventDefault();
25581         
25582         
25583         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25584         pane.tab.addClass('active');
25585         //Roo.log(pane.title);
25586         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25587         // technically we should have a deactivate event.. but maybe add later.
25588         // and it should not de-activate the selected tab...
25589         this.fireEvent('activatepane', pane);
25590         pane.el.addClass('active');
25591         pane.fireEvent('activate');
25592         
25593         
25594     },
25595     
25596     getActivePane : function()
25597     {
25598         var r = false;
25599         Roo.each(this.panes, function(p) {
25600             if(p.el.hasClass('active')){
25601                 r = p;
25602                 return false;
25603             }
25604             
25605             return;
25606         });
25607         
25608         return r;
25609     }
25610     
25611     
25612 });
25613
25614  
25615 /*
25616  * - LGPL
25617  *
25618  * Tab pane
25619  * 
25620  */
25621 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25622 /**
25623  * @class Roo.bootstrap.TabPane
25624  * @extends Roo.bootstrap.Component
25625  * Bootstrap TabPane class
25626  * @cfg {Boolean} active (false | true) Default false
25627  * @cfg {String} title title of panel
25628
25629  * 
25630  * @constructor
25631  * Create a new TabPane
25632  * @param {Object} config The config object
25633  */
25634
25635 Roo.bootstrap.dash.TabPane = function(config){
25636     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25637     
25638     this.addEvents({
25639         // raw events
25640         /**
25641          * @event activate
25642          * When a pane is activated
25643          * @param {Roo.bootstrap.dash.TabPane} pane
25644          */
25645         "activate" : true
25646          
25647     });
25648 };
25649
25650 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25651     
25652     active : false,
25653     title : '',
25654     
25655     // the tabBox that this is attached to.
25656     tab : false,
25657      
25658     getAutoCreate : function() 
25659     {
25660         var cfg = {
25661             tag: 'div',
25662             cls: 'tab-pane'
25663         };
25664         
25665         if(this.active){
25666             cfg.cls += ' active';
25667         }
25668         
25669         return cfg;
25670     },
25671     initEvents  : function()
25672     {
25673         //Roo.log('trigger add pane handler');
25674         this.parent().fireEvent('addpane', this)
25675     },
25676     
25677      /**
25678      * Updates the tab title 
25679      * @param {String} html to set the title to.
25680      */
25681     setTitle: function(str)
25682     {
25683         if (!this.tab) {
25684             return;
25685         }
25686         this.title = str;
25687         this.tab.select('a', true).first().dom.innerHTML = str;
25688         
25689     }
25690     
25691     
25692     
25693 });
25694
25695  
25696
25697
25698  /*
25699  * - LGPL
25700  *
25701  * menu
25702  * 
25703  */
25704 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25705
25706 /**
25707  * @class Roo.bootstrap.menu.Menu
25708  * @extends Roo.bootstrap.Component
25709  * Bootstrap Menu class - container for Menu
25710  * @cfg {String} html Text of the menu
25711  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25712  * @cfg {String} icon Font awesome icon
25713  * @cfg {String} pos Menu align to (top | bottom) default bottom
25714  * 
25715  * 
25716  * @constructor
25717  * Create a new Menu
25718  * @param {Object} config The config object
25719  */
25720
25721
25722 Roo.bootstrap.menu.Menu = function(config){
25723     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25724     
25725     this.addEvents({
25726         /**
25727          * @event beforeshow
25728          * Fires before this menu is displayed
25729          * @param {Roo.bootstrap.menu.Menu} this
25730          */
25731         beforeshow : true,
25732         /**
25733          * @event beforehide
25734          * Fires before this menu is hidden
25735          * @param {Roo.bootstrap.menu.Menu} this
25736          */
25737         beforehide : true,
25738         /**
25739          * @event show
25740          * Fires after this menu is displayed
25741          * @param {Roo.bootstrap.menu.Menu} this
25742          */
25743         show : true,
25744         /**
25745          * @event hide
25746          * Fires after this menu is hidden
25747          * @param {Roo.bootstrap.menu.Menu} this
25748          */
25749         hide : true,
25750         /**
25751          * @event click
25752          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25753          * @param {Roo.bootstrap.menu.Menu} this
25754          * @param {Roo.EventObject} e
25755          */
25756         click : true
25757     });
25758     
25759 };
25760
25761 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25762     
25763     submenu : false,
25764     html : '',
25765     weight : 'default',
25766     icon : false,
25767     pos : 'bottom',
25768     
25769     
25770     getChildContainer : function() {
25771         if(this.isSubMenu){
25772             return this.el;
25773         }
25774         
25775         return this.el.select('ul.dropdown-menu', true).first();  
25776     },
25777     
25778     getAutoCreate : function()
25779     {
25780         var text = [
25781             {
25782                 tag : 'span',
25783                 cls : 'roo-menu-text',
25784                 html : this.html
25785             }
25786         ];
25787         
25788         if(this.icon){
25789             text.unshift({
25790                 tag : 'i',
25791                 cls : 'fa ' + this.icon
25792             })
25793         }
25794         
25795         
25796         var cfg = {
25797             tag : 'div',
25798             cls : 'btn-group',
25799             cn : [
25800                 {
25801                     tag : 'button',
25802                     cls : 'dropdown-button btn btn-' + this.weight,
25803                     cn : text
25804                 },
25805                 {
25806                     tag : 'button',
25807                     cls : 'dropdown-toggle btn btn-' + this.weight,
25808                     cn : [
25809                         {
25810                             tag : 'span',
25811                             cls : 'caret'
25812                         }
25813                     ]
25814                 },
25815                 {
25816                     tag : 'ul',
25817                     cls : 'dropdown-menu'
25818                 }
25819             ]
25820             
25821         };
25822         
25823         if(this.pos == 'top'){
25824             cfg.cls += ' dropup';
25825         }
25826         
25827         if(this.isSubMenu){
25828             cfg = {
25829                 tag : 'ul',
25830                 cls : 'dropdown-menu'
25831             }
25832         }
25833         
25834         return cfg;
25835     },
25836     
25837     onRender : function(ct, position)
25838     {
25839         this.isSubMenu = ct.hasClass('dropdown-submenu');
25840         
25841         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25842     },
25843     
25844     initEvents : function() 
25845     {
25846         if(this.isSubMenu){
25847             return;
25848         }
25849         
25850         this.hidden = true;
25851         
25852         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25853         this.triggerEl.on('click', this.onTriggerPress, this);
25854         
25855         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25856         this.buttonEl.on('click', this.onClick, this);
25857         
25858     },
25859     
25860     list : function()
25861     {
25862         if(this.isSubMenu){
25863             return this.el;
25864         }
25865         
25866         return this.el.select('ul.dropdown-menu', true).first();
25867     },
25868     
25869     onClick : function(e)
25870     {
25871         this.fireEvent("click", this, e);
25872     },
25873     
25874     onTriggerPress  : function(e)
25875     {   
25876         if (this.isVisible()) {
25877             this.hide();
25878         } else {
25879             this.show();
25880         }
25881     },
25882     
25883     isVisible : function(){
25884         return !this.hidden;
25885     },
25886     
25887     show : function()
25888     {
25889         this.fireEvent("beforeshow", this);
25890         
25891         this.hidden = false;
25892         this.el.addClass('open');
25893         
25894         Roo.get(document).on("mouseup", this.onMouseUp, this);
25895         
25896         this.fireEvent("show", this);
25897         
25898         
25899     },
25900     
25901     hide : function()
25902     {
25903         this.fireEvent("beforehide", this);
25904         
25905         this.hidden = true;
25906         this.el.removeClass('open');
25907         
25908         Roo.get(document).un("mouseup", this.onMouseUp);
25909         
25910         this.fireEvent("hide", this);
25911     },
25912     
25913     onMouseUp : function()
25914     {
25915         this.hide();
25916     }
25917     
25918 });
25919
25920  
25921  /*
25922  * - LGPL
25923  *
25924  * menu item
25925  * 
25926  */
25927 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25928
25929 /**
25930  * @class Roo.bootstrap.menu.Item
25931  * @extends Roo.bootstrap.Component
25932  * Bootstrap MenuItem class
25933  * @cfg {Boolean} submenu (true | false) default false
25934  * @cfg {String} html text of the item
25935  * @cfg {String} href the link
25936  * @cfg {Boolean} disable (true | false) default false
25937  * @cfg {Boolean} preventDefault (true | false) default true
25938  * @cfg {String} icon Font awesome icon
25939  * @cfg {String} pos Submenu align to (left | right) default right 
25940  * 
25941  * 
25942  * @constructor
25943  * Create a new Item
25944  * @param {Object} config The config object
25945  */
25946
25947
25948 Roo.bootstrap.menu.Item = function(config){
25949     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25950     this.addEvents({
25951         /**
25952          * @event mouseover
25953          * Fires when the mouse is hovering over this menu
25954          * @param {Roo.bootstrap.menu.Item} this
25955          * @param {Roo.EventObject} e
25956          */
25957         mouseover : true,
25958         /**
25959          * @event mouseout
25960          * Fires when the mouse exits this menu
25961          * @param {Roo.bootstrap.menu.Item} this
25962          * @param {Roo.EventObject} e
25963          */
25964         mouseout : true,
25965         // raw events
25966         /**
25967          * @event click
25968          * The raw click event for the entire grid.
25969          * @param {Roo.EventObject} e
25970          */
25971         click : true
25972     });
25973 };
25974
25975 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25976     
25977     submenu : false,
25978     href : '',
25979     html : '',
25980     preventDefault: true,
25981     disable : false,
25982     icon : false,
25983     pos : 'right',
25984     
25985     getAutoCreate : function()
25986     {
25987         var text = [
25988             {
25989                 tag : 'span',
25990                 cls : 'roo-menu-item-text',
25991                 html : this.html
25992             }
25993         ];
25994         
25995         if(this.icon){
25996             text.unshift({
25997                 tag : 'i',
25998                 cls : 'fa ' + this.icon
25999             })
26000         }
26001         
26002         var cfg = {
26003             tag : 'li',
26004             cn : [
26005                 {
26006                     tag : 'a',
26007                     href : this.href || '#',
26008                     cn : text
26009                 }
26010             ]
26011         };
26012         
26013         if(this.disable){
26014             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26015         }
26016         
26017         if(this.submenu){
26018             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26019             
26020             if(this.pos == 'left'){
26021                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26022             }
26023         }
26024         
26025         return cfg;
26026     },
26027     
26028     initEvents : function() 
26029     {
26030         this.el.on('mouseover', this.onMouseOver, this);
26031         this.el.on('mouseout', this.onMouseOut, this);
26032         
26033         this.el.select('a', true).first().on('click', this.onClick, this);
26034         
26035     },
26036     
26037     onClick : function(e)
26038     {
26039         if(this.preventDefault){
26040             e.preventDefault();
26041         }
26042         
26043         this.fireEvent("click", this, e);
26044     },
26045     
26046     onMouseOver : function(e)
26047     {
26048         if(this.submenu && this.pos == 'left'){
26049             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26050         }
26051         
26052         this.fireEvent("mouseover", this, e);
26053     },
26054     
26055     onMouseOut : function(e)
26056     {
26057         this.fireEvent("mouseout", this, e);
26058     }
26059 });
26060
26061  
26062
26063  /*
26064  * - LGPL
26065  *
26066  * menu separator
26067  * 
26068  */
26069 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26070
26071 /**
26072  * @class Roo.bootstrap.menu.Separator
26073  * @extends Roo.bootstrap.Component
26074  * Bootstrap Separator class
26075  * 
26076  * @constructor
26077  * Create a new Separator
26078  * @param {Object} config The config object
26079  */
26080
26081
26082 Roo.bootstrap.menu.Separator = function(config){
26083     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26084 };
26085
26086 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26087     
26088     getAutoCreate : function(){
26089         var cfg = {
26090             tag : 'li',
26091             cls: 'divider'
26092         };
26093         
26094         return cfg;
26095     }
26096    
26097 });
26098
26099  
26100
26101  /*
26102  * - LGPL
26103  *
26104  * Tooltip
26105  * 
26106  */
26107
26108 /**
26109  * @class Roo.bootstrap.Tooltip
26110  * Bootstrap Tooltip class
26111  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26112  * to determine which dom element triggers the tooltip.
26113  * 
26114  * It needs to add support for additional attributes like tooltip-position
26115  * 
26116  * @constructor
26117  * Create a new Toolti
26118  * @param {Object} config The config object
26119  */
26120
26121 Roo.bootstrap.Tooltip = function(config){
26122     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26123     
26124     this.alignment = Roo.bootstrap.Tooltip.alignment;
26125     
26126     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26127         this.alignment = config.alignment;
26128     }
26129     
26130 };
26131
26132 Roo.apply(Roo.bootstrap.Tooltip, {
26133     /**
26134      * @function init initialize tooltip monitoring.
26135      * @static
26136      */
26137     currentEl : false,
26138     currentTip : false,
26139     currentRegion : false,
26140     
26141     //  init : delay?
26142     
26143     init : function()
26144     {
26145         Roo.get(document).on('mouseover', this.enter ,this);
26146         Roo.get(document).on('mouseout', this.leave, this);
26147          
26148         
26149         this.currentTip = new Roo.bootstrap.Tooltip();
26150     },
26151     
26152     enter : function(ev)
26153     {
26154         var dom = ev.getTarget();
26155         
26156         //Roo.log(['enter',dom]);
26157         var el = Roo.fly(dom);
26158         if (this.currentEl) {
26159             //Roo.log(dom);
26160             //Roo.log(this.currentEl);
26161             //Roo.log(this.currentEl.contains(dom));
26162             if (this.currentEl == el) {
26163                 return;
26164             }
26165             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26166                 return;
26167             }
26168
26169         }
26170         
26171         if (this.currentTip.el) {
26172             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26173         }    
26174         //Roo.log(ev);
26175         
26176         if(!el || el.dom == document){
26177             return;
26178         }
26179         
26180         var bindEl = el;
26181         
26182         // you can not look for children, as if el is the body.. then everythign is the child..
26183         if (!el.attr('tooltip')) { //
26184             if (!el.select("[tooltip]").elements.length) {
26185                 return;
26186             }
26187             // is the mouse over this child...?
26188             bindEl = el.select("[tooltip]").first();
26189             var xy = ev.getXY();
26190             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26191                 //Roo.log("not in region.");
26192                 return;
26193             }
26194             //Roo.log("child element over..");
26195             
26196         }
26197         this.currentEl = bindEl;
26198         this.currentTip.bind(bindEl);
26199         this.currentRegion = Roo.lib.Region.getRegion(dom);
26200         this.currentTip.enter();
26201         
26202     },
26203     leave : function(ev)
26204     {
26205         var dom = ev.getTarget();
26206         //Roo.log(['leave',dom]);
26207         if (!this.currentEl) {
26208             return;
26209         }
26210         
26211         
26212         if (dom != this.currentEl.dom) {
26213             return;
26214         }
26215         var xy = ev.getXY();
26216         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26217             return;
26218         }
26219         // only activate leave if mouse cursor is outside... bounding box..
26220         
26221         
26222         
26223         
26224         if (this.currentTip) {
26225             this.currentTip.leave();
26226         }
26227         //Roo.log('clear currentEl');
26228         this.currentEl = false;
26229         
26230         
26231     },
26232     alignment : {
26233         'left' : ['r-l', [-2,0], 'right'],
26234         'right' : ['l-r', [2,0], 'left'],
26235         'bottom' : ['t-b', [0,2], 'top'],
26236         'top' : [ 'b-t', [0,-2], 'bottom']
26237     }
26238     
26239 });
26240
26241
26242 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26243     
26244     
26245     bindEl : false,
26246     
26247     delay : null, // can be { show : 300 , hide: 500}
26248     
26249     timeout : null,
26250     
26251     hoverState : null, //???
26252     
26253     placement : 'bottom', 
26254     
26255     alignment : false,
26256     
26257     getAutoCreate : function(){
26258     
26259         var cfg = {
26260            cls : 'tooltip',
26261            role : 'tooltip',
26262            cn : [
26263                 {
26264                     cls : 'tooltip-arrow'
26265                 },
26266                 {
26267                     cls : 'tooltip-inner'
26268                 }
26269            ]
26270         };
26271         
26272         return cfg;
26273     },
26274     bind : function(el)
26275     {
26276         this.bindEl = el;
26277     },
26278       
26279     
26280     enter : function () {
26281        
26282         if (this.timeout != null) {
26283             clearTimeout(this.timeout);
26284         }
26285         
26286         this.hoverState = 'in';
26287          //Roo.log("enter - show");
26288         if (!this.delay || !this.delay.show) {
26289             this.show();
26290             return;
26291         }
26292         var _t = this;
26293         this.timeout = setTimeout(function () {
26294             if (_t.hoverState == 'in') {
26295                 _t.show();
26296             }
26297         }, this.delay.show);
26298     },
26299     leave : function()
26300     {
26301         clearTimeout(this.timeout);
26302     
26303         this.hoverState = 'out';
26304          if (!this.delay || !this.delay.hide) {
26305             this.hide();
26306             return;
26307         }
26308        
26309         var _t = this;
26310         this.timeout = setTimeout(function () {
26311             //Roo.log("leave - timeout");
26312             
26313             if (_t.hoverState == 'out') {
26314                 _t.hide();
26315                 Roo.bootstrap.Tooltip.currentEl = false;
26316             }
26317         }, delay);
26318     },
26319     
26320     show : function (msg)
26321     {
26322         if (!this.el) {
26323             this.render(document.body);
26324         }
26325         // set content.
26326         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26327         
26328         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26329         
26330         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26331         
26332         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26333         
26334         var placement = typeof this.placement == 'function' ?
26335             this.placement.call(this, this.el, on_el) :
26336             this.placement;
26337             
26338         var autoToken = /\s?auto?\s?/i;
26339         var autoPlace = autoToken.test(placement);
26340         if (autoPlace) {
26341             placement = placement.replace(autoToken, '') || 'top';
26342         }
26343         
26344         //this.el.detach()
26345         //this.el.setXY([0,0]);
26346         this.el.show();
26347         //this.el.dom.style.display='block';
26348         
26349         //this.el.appendTo(on_el);
26350         
26351         var p = this.getPosition();
26352         var box = this.el.getBox();
26353         
26354         if (autoPlace) {
26355             // fixme..
26356         }
26357         
26358         var align = this.alignment[placement];
26359         
26360         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26361         
26362         if(placement == 'top' || placement == 'bottom'){
26363             if(xy[0] < 0){
26364                 placement = 'right';
26365             }
26366             
26367             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26368                 placement = 'left';
26369             }
26370             
26371             var scroll = Roo.select('body', true).first().getScroll();
26372             
26373             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26374                 placement = 'top';
26375             }
26376             
26377             align = this.alignment[placement];
26378         }
26379         
26380         this.el.alignTo(this.bindEl, align[0],align[1]);
26381         //var arrow = this.el.select('.arrow',true).first();
26382         //arrow.set(align[2], 
26383         
26384         this.el.addClass(placement);
26385         
26386         this.el.addClass('in fade');
26387         
26388         this.hoverState = null;
26389         
26390         if (this.el.hasClass('fade')) {
26391             // fade it?
26392         }
26393         
26394     },
26395     hide : function()
26396     {
26397          
26398         if (!this.el) {
26399             return;
26400         }
26401         //this.el.setXY([0,0]);
26402         this.el.removeClass('in');
26403         //this.el.hide();
26404         
26405     }
26406     
26407 });
26408  
26409
26410  /*
26411  * - LGPL
26412  *
26413  * Location Picker
26414  * 
26415  */
26416
26417 /**
26418  * @class Roo.bootstrap.LocationPicker
26419  * @extends Roo.bootstrap.Component
26420  * Bootstrap LocationPicker class
26421  * @cfg {Number} latitude Position when init default 0
26422  * @cfg {Number} longitude Position when init default 0
26423  * @cfg {Number} zoom default 15
26424  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26425  * @cfg {Boolean} mapTypeControl default false
26426  * @cfg {Boolean} disableDoubleClickZoom default false
26427  * @cfg {Boolean} scrollwheel default true
26428  * @cfg {Boolean} streetViewControl default false
26429  * @cfg {Number} radius default 0
26430  * @cfg {String} locationName
26431  * @cfg {Boolean} draggable default true
26432  * @cfg {Boolean} enableAutocomplete default false
26433  * @cfg {Boolean} enableReverseGeocode default true
26434  * @cfg {String} markerTitle
26435  * 
26436  * @constructor
26437  * Create a new LocationPicker
26438  * @param {Object} config The config object
26439  */
26440
26441
26442 Roo.bootstrap.LocationPicker = function(config){
26443     
26444     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26445     
26446     this.addEvents({
26447         /**
26448          * @event initial
26449          * Fires when the picker initialized.
26450          * @param {Roo.bootstrap.LocationPicker} this
26451          * @param {Google Location} location
26452          */
26453         initial : true,
26454         /**
26455          * @event positionchanged
26456          * Fires when the picker position changed.
26457          * @param {Roo.bootstrap.LocationPicker} this
26458          * @param {Google Location} location
26459          */
26460         positionchanged : true,
26461         /**
26462          * @event resize
26463          * Fires when the map resize.
26464          * @param {Roo.bootstrap.LocationPicker} this
26465          */
26466         resize : true,
26467         /**
26468          * @event show
26469          * Fires when the map show.
26470          * @param {Roo.bootstrap.LocationPicker} this
26471          */
26472         show : true,
26473         /**
26474          * @event hide
26475          * Fires when the map hide.
26476          * @param {Roo.bootstrap.LocationPicker} this
26477          */
26478         hide : true,
26479         /**
26480          * @event mapClick
26481          * Fires when click the map.
26482          * @param {Roo.bootstrap.LocationPicker} this
26483          * @param {Map event} e
26484          */
26485         mapClick : true,
26486         /**
26487          * @event mapRightClick
26488          * Fires when right click the map.
26489          * @param {Roo.bootstrap.LocationPicker} this
26490          * @param {Map event} e
26491          */
26492         mapRightClick : true,
26493         /**
26494          * @event markerClick
26495          * Fires when click the marker.
26496          * @param {Roo.bootstrap.LocationPicker} this
26497          * @param {Map event} e
26498          */
26499         markerClick : true,
26500         /**
26501          * @event markerRightClick
26502          * Fires when right click the marker.
26503          * @param {Roo.bootstrap.LocationPicker} this
26504          * @param {Map event} e
26505          */
26506         markerRightClick : true,
26507         /**
26508          * @event OverlayViewDraw
26509          * Fires when OverlayView Draw
26510          * @param {Roo.bootstrap.LocationPicker} this
26511          */
26512         OverlayViewDraw : true,
26513         /**
26514          * @event OverlayViewOnAdd
26515          * Fires when OverlayView Draw
26516          * @param {Roo.bootstrap.LocationPicker} this
26517          */
26518         OverlayViewOnAdd : true,
26519         /**
26520          * @event OverlayViewOnRemove
26521          * Fires when OverlayView Draw
26522          * @param {Roo.bootstrap.LocationPicker} this
26523          */
26524         OverlayViewOnRemove : true,
26525         /**
26526          * @event OverlayViewShow
26527          * Fires when OverlayView Draw
26528          * @param {Roo.bootstrap.LocationPicker} this
26529          * @param {Pixel} cpx
26530          */
26531         OverlayViewShow : true,
26532         /**
26533          * @event OverlayViewHide
26534          * Fires when OverlayView Draw
26535          * @param {Roo.bootstrap.LocationPicker} this
26536          */
26537         OverlayViewHide : true,
26538         /**
26539          * @event loadexception
26540          * Fires when load google lib failed.
26541          * @param {Roo.bootstrap.LocationPicker} this
26542          */
26543         loadexception : true
26544     });
26545         
26546 };
26547
26548 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26549     
26550     gMapContext: false,
26551     
26552     latitude: 0,
26553     longitude: 0,
26554     zoom: 15,
26555     mapTypeId: false,
26556     mapTypeControl: false,
26557     disableDoubleClickZoom: false,
26558     scrollwheel: true,
26559     streetViewControl: false,
26560     radius: 0,
26561     locationName: '',
26562     draggable: true,
26563     enableAutocomplete: false,
26564     enableReverseGeocode: true,
26565     markerTitle: '',
26566     
26567     getAutoCreate: function()
26568     {
26569
26570         var cfg = {
26571             tag: 'div',
26572             cls: 'roo-location-picker'
26573         };
26574         
26575         return cfg
26576     },
26577     
26578     initEvents: function(ct, position)
26579     {       
26580         if(!this.el.getWidth() || this.isApplied()){
26581             return;
26582         }
26583         
26584         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26585         
26586         this.initial();
26587     },
26588     
26589     initial: function()
26590     {
26591         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26592             this.fireEvent('loadexception', this);
26593             return;
26594         }
26595         
26596         if(!this.mapTypeId){
26597             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26598         }
26599         
26600         this.gMapContext = this.GMapContext();
26601         
26602         this.initOverlayView();
26603         
26604         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26605         
26606         var _this = this;
26607                 
26608         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26609             _this.setPosition(_this.gMapContext.marker.position);
26610         });
26611         
26612         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26613             _this.fireEvent('mapClick', this, event);
26614             
26615         });
26616
26617         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26618             _this.fireEvent('mapRightClick', this, event);
26619             
26620         });
26621         
26622         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26623             _this.fireEvent('markerClick', this, event);
26624             
26625         });
26626
26627         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26628             _this.fireEvent('markerRightClick', this, event);
26629             
26630         });
26631         
26632         this.setPosition(this.gMapContext.location);
26633         
26634         this.fireEvent('initial', this, this.gMapContext.location);
26635     },
26636     
26637     initOverlayView: function()
26638     {
26639         var _this = this;
26640         
26641         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26642             
26643             draw: function()
26644             {
26645                 _this.fireEvent('OverlayViewDraw', _this);
26646             },
26647             
26648             onAdd: function()
26649             {
26650                 _this.fireEvent('OverlayViewOnAdd', _this);
26651             },
26652             
26653             onRemove: function()
26654             {
26655                 _this.fireEvent('OverlayViewOnRemove', _this);
26656             },
26657             
26658             show: function(cpx)
26659             {
26660                 _this.fireEvent('OverlayViewShow', _this, cpx);
26661             },
26662             
26663             hide: function()
26664             {
26665                 _this.fireEvent('OverlayViewHide', _this);
26666             }
26667             
26668         });
26669     },
26670     
26671     fromLatLngToContainerPixel: function(event)
26672     {
26673         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26674     },
26675     
26676     isApplied: function() 
26677     {
26678         return this.getGmapContext() == false ? false : true;
26679     },
26680     
26681     getGmapContext: function() 
26682     {
26683         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26684     },
26685     
26686     GMapContext: function() 
26687     {
26688         var position = new google.maps.LatLng(this.latitude, this.longitude);
26689         
26690         var _map = new google.maps.Map(this.el.dom, {
26691             center: position,
26692             zoom: this.zoom,
26693             mapTypeId: this.mapTypeId,
26694             mapTypeControl: this.mapTypeControl,
26695             disableDoubleClickZoom: this.disableDoubleClickZoom,
26696             scrollwheel: this.scrollwheel,
26697             streetViewControl: this.streetViewControl,
26698             locationName: this.locationName,
26699             draggable: this.draggable,
26700             enableAutocomplete: this.enableAutocomplete,
26701             enableReverseGeocode: this.enableReverseGeocode
26702         });
26703         
26704         var _marker = new google.maps.Marker({
26705             position: position,
26706             map: _map,
26707             title: this.markerTitle,
26708             draggable: this.draggable
26709         });
26710         
26711         return {
26712             map: _map,
26713             marker: _marker,
26714             circle: null,
26715             location: position,
26716             radius: this.radius,
26717             locationName: this.locationName,
26718             addressComponents: {
26719                 formatted_address: null,
26720                 addressLine1: null,
26721                 addressLine2: null,
26722                 streetName: null,
26723                 streetNumber: null,
26724                 city: null,
26725                 district: null,
26726                 state: null,
26727                 stateOrProvince: null
26728             },
26729             settings: this,
26730             domContainer: this.el.dom,
26731             geodecoder: new google.maps.Geocoder()
26732         };
26733     },
26734     
26735     drawCircle: function(center, radius, options) 
26736     {
26737         if (this.gMapContext.circle != null) {
26738             this.gMapContext.circle.setMap(null);
26739         }
26740         if (radius > 0) {
26741             radius *= 1;
26742             options = Roo.apply({}, options, {
26743                 strokeColor: "#0000FF",
26744                 strokeOpacity: .35,
26745                 strokeWeight: 2,
26746                 fillColor: "#0000FF",
26747                 fillOpacity: .2
26748             });
26749             
26750             options.map = this.gMapContext.map;
26751             options.radius = radius;
26752             options.center = center;
26753             this.gMapContext.circle = new google.maps.Circle(options);
26754             return this.gMapContext.circle;
26755         }
26756         
26757         return null;
26758     },
26759     
26760     setPosition: function(location) 
26761     {
26762         this.gMapContext.location = location;
26763         this.gMapContext.marker.setPosition(location);
26764         this.gMapContext.map.panTo(location);
26765         this.drawCircle(location, this.gMapContext.radius, {});
26766         
26767         var _this = this;
26768         
26769         if (this.gMapContext.settings.enableReverseGeocode) {
26770             this.gMapContext.geodecoder.geocode({
26771                 latLng: this.gMapContext.location
26772             }, function(results, status) {
26773                 
26774                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26775                     _this.gMapContext.locationName = results[0].formatted_address;
26776                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26777                     
26778                     _this.fireEvent('positionchanged', this, location);
26779                 }
26780             });
26781             
26782             return;
26783         }
26784         
26785         this.fireEvent('positionchanged', this, location);
26786     },
26787     
26788     resize: function()
26789     {
26790         google.maps.event.trigger(this.gMapContext.map, "resize");
26791         
26792         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26793         
26794         this.fireEvent('resize', this);
26795     },
26796     
26797     setPositionByLatLng: function(latitude, longitude)
26798     {
26799         this.setPosition(new google.maps.LatLng(latitude, longitude));
26800     },
26801     
26802     getCurrentPosition: function() 
26803     {
26804         return {
26805             latitude: this.gMapContext.location.lat(),
26806             longitude: this.gMapContext.location.lng()
26807         };
26808     },
26809     
26810     getAddressName: function() 
26811     {
26812         return this.gMapContext.locationName;
26813     },
26814     
26815     getAddressComponents: function() 
26816     {
26817         return this.gMapContext.addressComponents;
26818     },
26819     
26820     address_component_from_google_geocode: function(address_components) 
26821     {
26822         var result = {};
26823         
26824         for (var i = 0; i < address_components.length; i++) {
26825             var component = address_components[i];
26826             if (component.types.indexOf("postal_code") >= 0) {
26827                 result.postalCode = component.short_name;
26828             } else if (component.types.indexOf("street_number") >= 0) {
26829                 result.streetNumber = component.short_name;
26830             } else if (component.types.indexOf("route") >= 0) {
26831                 result.streetName = component.short_name;
26832             } else if (component.types.indexOf("neighborhood") >= 0) {
26833                 result.city = component.short_name;
26834             } else if (component.types.indexOf("locality") >= 0) {
26835                 result.city = component.short_name;
26836             } else if (component.types.indexOf("sublocality") >= 0) {
26837                 result.district = component.short_name;
26838             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26839                 result.stateOrProvince = component.short_name;
26840             } else if (component.types.indexOf("country") >= 0) {
26841                 result.country = component.short_name;
26842             }
26843         }
26844         
26845         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26846         result.addressLine2 = "";
26847         return result;
26848     },
26849     
26850     setZoomLevel: function(zoom)
26851     {
26852         this.gMapContext.map.setZoom(zoom);
26853     },
26854     
26855     show: function()
26856     {
26857         if(!this.el){
26858             return;
26859         }
26860         
26861         this.el.show();
26862         
26863         this.resize();
26864         
26865         this.fireEvent('show', this);
26866     },
26867     
26868     hide: function()
26869     {
26870         if(!this.el){
26871             return;
26872         }
26873         
26874         this.el.hide();
26875         
26876         this.fireEvent('hide', this);
26877     }
26878     
26879 });
26880
26881 Roo.apply(Roo.bootstrap.LocationPicker, {
26882     
26883     OverlayView : function(map, options)
26884     {
26885         options = options || {};
26886         
26887         this.setMap(map);
26888     }
26889     
26890     
26891 });/*
26892  * - LGPL
26893  *
26894  * Alert
26895  * 
26896  */
26897
26898 /**
26899  * @class Roo.bootstrap.Alert
26900  * @extends Roo.bootstrap.Component
26901  * Bootstrap Alert class
26902  * @cfg {String} title The title of alert
26903  * @cfg {String} html The content of alert
26904  * @cfg {String} weight (  success | info | warning | danger )
26905  * @cfg {String} faicon font-awesomeicon
26906  * 
26907  * @constructor
26908  * Create a new alert
26909  * @param {Object} config The config object
26910  */
26911
26912
26913 Roo.bootstrap.Alert = function(config){
26914     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26915     
26916 };
26917
26918 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26919     
26920     title: '',
26921     html: '',
26922     weight: false,
26923     faicon: false,
26924     
26925     getAutoCreate : function()
26926     {
26927         
26928         var cfg = {
26929             tag : 'div',
26930             cls : 'alert',
26931             cn : [
26932                 {
26933                     tag : 'i',
26934                     cls : 'roo-alert-icon'
26935                     
26936                 },
26937                 {
26938                     tag : 'b',
26939                     cls : 'roo-alert-title',
26940                     html : this.title
26941                 },
26942                 {
26943                     tag : 'span',
26944                     cls : 'roo-alert-text',
26945                     html : this.html
26946                 }
26947             ]
26948         };
26949         
26950         if(this.faicon){
26951             cfg.cn[0].cls += ' fa ' + this.faicon;
26952         }
26953         
26954         if(this.weight){
26955             cfg.cls += ' alert-' + this.weight;
26956         }
26957         
26958         return cfg;
26959     },
26960     
26961     initEvents: function() 
26962     {
26963         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26964     },
26965     
26966     setTitle : function(str)
26967     {
26968         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26969     },
26970     
26971     setText : function(str)
26972     {
26973         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26974     },
26975     
26976     setWeight : function(weight)
26977     {
26978         if(this.weight){
26979             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26980         }
26981         
26982         this.weight = weight;
26983         
26984         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26985     },
26986     
26987     setIcon : function(icon)
26988     {
26989         if(this.faicon){
26990             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26991         }
26992         
26993         this.faicon = icon;
26994         
26995         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26996     },
26997     
26998     hide: function() 
26999     {
27000         this.el.hide();   
27001     },
27002     
27003     show: function() 
27004     {  
27005         this.el.show();   
27006     }
27007     
27008 });
27009
27010  
27011 /*
27012 * Licence: LGPL
27013 */
27014
27015 /**
27016  * @class Roo.bootstrap.UploadCropbox
27017  * @extends Roo.bootstrap.Component
27018  * Bootstrap UploadCropbox class
27019  * @cfg {String} emptyText show when image has been loaded
27020  * @cfg {String} rotateNotify show when image too small to rotate
27021  * @cfg {Number} errorTimeout default 3000
27022  * @cfg {Number} minWidth default 300
27023  * @cfg {Number} minHeight default 300
27024  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27025  * @cfg {Boolean} isDocument (true|false) default false
27026  * @cfg {String} url action url
27027  * @cfg {String} paramName default 'imageUpload'
27028  * @cfg {String} method default POST
27029  * @cfg {Boolean} loadMask (true|false) default true
27030  * @cfg {Boolean} loadingText default 'Loading...'
27031  * 
27032  * @constructor
27033  * Create a new UploadCropbox
27034  * @param {Object} config The config object
27035  */
27036
27037 Roo.bootstrap.UploadCropbox = function(config){
27038     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27039     
27040     this.addEvents({
27041         /**
27042          * @event beforeselectfile
27043          * Fire before select file
27044          * @param {Roo.bootstrap.UploadCropbox} this
27045          */
27046         "beforeselectfile" : true,
27047         /**
27048          * @event initial
27049          * Fire after initEvent
27050          * @param {Roo.bootstrap.UploadCropbox} this
27051          */
27052         "initial" : true,
27053         /**
27054          * @event crop
27055          * Fire after initEvent
27056          * @param {Roo.bootstrap.UploadCropbox} this
27057          * @param {String} data
27058          */
27059         "crop" : true,
27060         /**
27061          * @event prepare
27062          * Fire when preparing the file data
27063          * @param {Roo.bootstrap.UploadCropbox} this
27064          * @param {Object} file
27065          */
27066         "prepare" : true,
27067         /**
27068          * @event exception
27069          * Fire when get exception
27070          * @param {Roo.bootstrap.UploadCropbox} this
27071          * @param {XMLHttpRequest} xhr
27072          */
27073         "exception" : true,
27074         /**
27075          * @event beforeloadcanvas
27076          * Fire before load the canvas
27077          * @param {Roo.bootstrap.UploadCropbox} this
27078          * @param {String} src
27079          */
27080         "beforeloadcanvas" : true,
27081         /**
27082          * @event trash
27083          * Fire when trash image
27084          * @param {Roo.bootstrap.UploadCropbox} this
27085          */
27086         "trash" : true,
27087         /**
27088          * @event download
27089          * Fire when download the image
27090          * @param {Roo.bootstrap.UploadCropbox} this
27091          */
27092         "download" : true,
27093         /**
27094          * @event footerbuttonclick
27095          * Fire when footerbuttonclick
27096          * @param {Roo.bootstrap.UploadCropbox} this
27097          * @param {String} type
27098          */
27099         "footerbuttonclick" : true,
27100         /**
27101          * @event resize
27102          * Fire when resize
27103          * @param {Roo.bootstrap.UploadCropbox} this
27104          */
27105         "resize" : true,
27106         /**
27107          * @event rotate
27108          * Fire when rotate the image
27109          * @param {Roo.bootstrap.UploadCropbox} this
27110          * @param {String} pos
27111          */
27112         "rotate" : true,
27113         /**
27114          * @event inspect
27115          * Fire when inspect the file
27116          * @param {Roo.bootstrap.UploadCropbox} this
27117          * @param {Object} file
27118          */
27119         "inspect" : true,
27120         /**
27121          * @event upload
27122          * Fire when xhr upload the file
27123          * @param {Roo.bootstrap.UploadCropbox} this
27124          * @param {Object} data
27125          */
27126         "upload" : true,
27127         /**
27128          * @event arrange
27129          * Fire when arrange the file data
27130          * @param {Roo.bootstrap.UploadCropbox} this
27131          * @param {Object} formData
27132          */
27133         "arrange" : true
27134     });
27135     
27136     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27137 };
27138
27139 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27140     
27141     emptyText : 'Click to upload image',
27142     rotateNotify : 'Image is too small to rotate',
27143     errorTimeout : 3000,
27144     scale : 0,
27145     baseScale : 1,
27146     rotate : 0,
27147     dragable : false,
27148     pinching : false,
27149     mouseX : 0,
27150     mouseY : 0,
27151     cropData : false,
27152     minWidth : 300,
27153     minHeight : 300,
27154     file : false,
27155     exif : {},
27156     baseRotate : 1,
27157     cropType : 'image/jpeg',
27158     buttons : false,
27159     canvasLoaded : false,
27160     isDocument : false,
27161     method : 'POST',
27162     paramName : 'imageUpload',
27163     loadMask : true,
27164     loadingText : 'Loading...',
27165     maskEl : false,
27166     
27167     getAutoCreate : function()
27168     {
27169         var cfg = {
27170             tag : 'div',
27171             cls : 'roo-upload-cropbox',
27172             cn : [
27173                 {
27174                     tag : 'input',
27175                     cls : 'roo-upload-cropbox-selector',
27176                     type : 'file'
27177                 },
27178                 {
27179                     tag : 'div',
27180                     cls : 'roo-upload-cropbox-body',
27181                     style : 'cursor:pointer',
27182                     cn : [
27183                         {
27184                             tag : 'div',
27185                             cls : 'roo-upload-cropbox-preview'
27186                         },
27187                         {
27188                             tag : 'div',
27189                             cls : 'roo-upload-cropbox-thumb'
27190                         },
27191                         {
27192                             tag : 'div',
27193                             cls : 'roo-upload-cropbox-empty-notify',
27194                             html : this.emptyText
27195                         },
27196                         {
27197                             tag : 'div',
27198                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27199                             html : this.rotateNotify
27200                         }
27201                     ]
27202                 },
27203                 {
27204                     tag : 'div',
27205                     cls : 'roo-upload-cropbox-footer',
27206                     cn : {
27207                         tag : 'div',
27208                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27209                         cn : []
27210                     }
27211                 }
27212             ]
27213         };
27214         
27215         return cfg;
27216     },
27217     
27218     onRender : function(ct, position)
27219     {
27220         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27221         
27222         if (this.buttons.length) {
27223             
27224             Roo.each(this.buttons, function(bb) {
27225                 
27226                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27227                 
27228                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27229                 
27230             }, this);
27231         }
27232         
27233         if(this.loadMask){
27234             this.maskEl = this.el;
27235         }
27236     },
27237     
27238     initEvents : function()
27239     {
27240         this.urlAPI = (window.createObjectURL && window) || 
27241                                 (window.URL && URL.revokeObjectURL && URL) || 
27242                                 (window.webkitURL && webkitURL);
27243                         
27244         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27245         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27246         
27247         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27248         this.selectorEl.hide();
27249         
27250         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27251         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27252         
27253         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27254         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27255         this.thumbEl.hide();
27256         
27257         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27258         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27259         
27260         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27261         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27262         this.errorEl.hide();
27263         
27264         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27265         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27266         this.footerEl.hide();
27267         
27268         this.setThumbBoxSize();
27269         
27270         this.bind();
27271         
27272         this.resize();
27273         
27274         this.fireEvent('initial', this);
27275     },
27276
27277     bind : function()
27278     {
27279         var _this = this;
27280         
27281         window.addEventListener("resize", function() { _this.resize(); } );
27282         
27283         this.bodyEl.on('click', this.beforeSelectFile, this);
27284         
27285         if(Roo.isTouch){
27286             this.bodyEl.on('touchstart', this.onTouchStart, this);
27287             this.bodyEl.on('touchmove', this.onTouchMove, this);
27288             this.bodyEl.on('touchend', this.onTouchEnd, this);
27289         }
27290         
27291         if(!Roo.isTouch){
27292             this.bodyEl.on('mousedown', this.onMouseDown, this);
27293             this.bodyEl.on('mousemove', this.onMouseMove, this);
27294             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27295             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27296             Roo.get(document).on('mouseup', this.onMouseUp, this);
27297         }
27298         
27299         this.selectorEl.on('change', this.onFileSelected, this);
27300     },
27301     
27302     reset : function()
27303     {    
27304         this.scale = 0;
27305         this.baseScale = 1;
27306         this.rotate = 0;
27307         this.baseRotate = 1;
27308         this.dragable = false;
27309         this.pinching = false;
27310         this.mouseX = 0;
27311         this.mouseY = 0;
27312         this.cropData = false;
27313         this.notifyEl.dom.innerHTML = this.emptyText;
27314         
27315         this.selectorEl.dom.value = '';
27316         
27317     },
27318     
27319     resize : function()
27320     {
27321         if(this.fireEvent('resize', this) != false){
27322             this.setThumbBoxPosition();
27323             this.setCanvasPosition();
27324         }
27325     },
27326     
27327     onFooterButtonClick : function(e, el, o, type)
27328     {
27329         switch (type) {
27330             case 'rotate-left' :
27331                 this.onRotateLeft(e);
27332                 break;
27333             case 'rotate-right' :
27334                 this.onRotateRight(e);
27335                 break;
27336             case 'picture' :
27337                 this.beforeSelectFile(e);
27338                 break;
27339             case 'trash' :
27340                 this.trash(e);
27341                 break;
27342             case 'crop' :
27343                 this.crop(e);
27344                 break;
27345             case 'download' :
27346                 this.download(e);
27347                 break;
27348             default :
27349                 break;
27350         }
27351         
27352         this.fireEvent('footerbuttonclick', this, type);
27353     },
27354     
27355     beforeSelectFile : function(e)
27356     {
27357         e.preventDefault();
27358         
27359         if(this.fireEvent('beforeselectfile', this) != false){
27360             this.selectorEl.dom.click();
27361         }
27362     },
27363     
27364     onFileSelected : function(e)
27365     {
27366         e.preventDefault();
27367         
27368         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27369             return;
27370         }
27371         
27372         var file = this.selectorEl.dom.files[0];
27373         
27374         if(this.fireEvent('inspect', this, file) != false){
27375             this.prepare(file);
27376         }
27377         
27378     },
27379     
27380     trash : function(e)
27381     {
27382         this.fireEvent('trash', this);
27383     },
27384     
27385     download : function(e)
27386     {
27387         this.fireEvent('download', this);
27388     },
27389     
27390     loadCanvas : function(src)
27391     {   
27392         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27393             
27394             this.reset();
27395             
27396             this.imageEl = document.createElement('img');
27397             
27398             var _this = this;
27399             
27400             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27401             
27402             this.imageEl.src = src;
27403         }
27404     },
27405     
27406     onLoadCanvas : function()
27407     {   
27408         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27409         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27410         
27411         this.bodyEl.un('click', this.beforeSelectFile, this);
27412         
27413         this.notifyEl.hide();
27414         this.thumbEl.show();
27415         this.footerEl.show();
27416         
27417         this.baseRotateLevel();
27418         
27419         if(this.isDocument){
27420             this.setThumbBoxSize();
27421         }
27422         
27423         this.setThumbBoxPosition();
27424         
27425         this.baseScaleLevel();
27426         
27427         this.draw();
27428         
27429         this.resize();
27430         
27431         this.canvasLoaded = true;
27432         
27433         if(this.loadMask){
27434             this.maskEl.unmask();
27435         }
27436         
27437     },
27438     
27439     setCanvasPosition : function()
27440     {   
27441         if(!this.canvasEl){
27442             return;
27443         }
27444         
27445         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27446         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27447         
27448         this.previewEl.setLeft(pw);
27449         this.previewEl.setTop(ph);
27450         
27451     },
27452     
27453     onMouseDown : function(e)
27454     {   
27455         e.stopEvent();
27456         
27457         this.dragable = true;
27458         this.pinching = false;
27459         
27460         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27461             this.dragable = false;
27462             return;
27463         }
27464         
27465         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27466         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27467         
27468     },
27469     
27470     onMouseMove : function(e)
27471     {   
27472         e.stopEvent();
27473         
27474         if(!this.canvasLoaded){
27475             return;
27476         }
27477         
27478         if (!this.dragable){
27479             return;
27480         }
27481         
27482         var minX = Math.ceil(this.thumbEl.getLeft(true));
27483         var minY = Math.ceil(this.thumbEl.getTop(true));
27484         
27485         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27486         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27487         
27488         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27489         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27490         
27491         x = x - this.mouseX;
27492         y = y - this.mouseY;
27493         
27494         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27495         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27496         
27497         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27498         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27499         
27500         this.previewEl.setLeft(bgX);
27501         this.previewEl.setTop(bgY);
27502         
27503         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27504         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27505     },
27506     
27507     onMouseUp : function(e)
27508     {   
27509         e.stopEvent();
27510         
27511         this.dragable = false;
27512     },
27513     
27514     onMouseWheel : function(e)
27515     {   
27516         e.stopEvent();
27517         
27518         this.startScale = this.scale;
27519         
27520         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27521         
27522         if(!this.zoomable()){
27523             this.scale = this.startScale;
27524             return;
27525         }
27526         
27527         this.draw();
27528         
27529         return;
27530     },
27531     
27532     zoomable : function()
27533     {
27534         var minScale = this.thumbEl.getWidth() / this.minWidth;
27535         
27536         if(this.minWidth < this.minHeight){
27537             minScale = this.thumbEl.getHeight() / this.minHeight;
27538         }
27539         
27540         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27541         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27542         
27543         if(
27544                 this.isDocument &&
27545                 (this.rotate == 0 || this.rotate == 180) && 
27546                 (
27547                     width > this.imageEl.OriginWidth || 
27548                     height > this.imageEl.OriginHeight ||
27549                     (width < this.minWidth && height < this.minHeight)
27550                 )
27551         ){
27552             return false;
27553         }
27554         
27555         if(
27556                 this.isDocument &&
27557                 (this.rotate == 90 || this.rotate == 270) && 
27558                 (
27559                     width > this.imageEl.OriginWidth || 
27560                     height > this.imageEl.OriginHeight ||
27561                     (width < this.minHeight && height < this.minWidth)
27562                 )
27563         ){
27564             return false;
27565         }
27566         
27567         if(
27568                 !this.isDocument &&
27569                 (this.rotate == 0 || this.rotate == 180) && 
27570                 (
27571                     width < this.minWidth || 
27572                     width > this.imageEl.OriginWidth || 
27573                     height < this.minHeight || 
27574                     height > this.imageEl.OriginHeight
27575                 )
27576         ){
27577             return false;
27578         }
27579         
27580         if(
27581                 !this.isDocument &&
27582                 (this.rotate == 90 || this.rotate == 270) && 
27583                 (
27584                     width < this.minHeight || 
27585                     width > this.imageEl.OriginWidth || 
27586                     height < this.minWidth || 
27587                     height > this.imageEl.OriginHeight
27588                 )
27589         ){
27590             return false;
27591         }
27592         
27593         return true;
27594         
27595     },
27596     
27597     onRotateLeft : function(e)
27598     {   
27599         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27600             
27601             var minScale = this.thumbEl.getWidth() / this.minWidth;
27602             
27603             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27604             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27605             
27606             this.startScale = this.scale;
27607             
27608             while (this.getScaleLevel() < minScale){
27609             
27610                 this.scale = this.scale + 1;
27611                 
27612                 if(!this.zoomable()){
27613                     break;
27614                 }
27615                 
27616                 if(
27617                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27618                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27619                 ){
27620                     continue;
27621                 }
27622                 
27623                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27624
27625                 this.draw();
27626                 
27627                 return;
27628             }
27629             
27630             this.scale = this.startScale;
27631             
27632             this.onRotateFail();
27633             
27634             return false;
27635         }
27636         
27637         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27638
27639         if(this.isDocument){
27640             this.setThumbBoxSize();
27641             this.setThumbBoxPosition();
27642             this.setCanvasPosition();
27643         }
27644         
27645         this.draw();
27646         
27647         this.fireEvent('rotate', this, 'left');
27648         
27649     },
27650     
27651     onRotateRight : function(e)
27652     {
27653         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27654             
27655             var minScale = this.thumbEl.getWidth() / this.minWidth;
27656         
27657             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27658             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27659             
27660             this.startScale = this.scale;
27661             
27662             while (this.getScaleLevel() < minScale){
27663             
27664                 this.scale = this.scale + 1;
27665                 
27666                 if(!this.zoomable()){
27667                     break;
27668                 }
27669                 
27670                 if(
27671                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27672                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27673                 ){
27674                     continue;
27675                 }
27676                 
27677                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27678
27679                 this.draw();
27680                 
27681                 return;
27682             }
27683             
27684             this.scale = this.startScale;
27685             
27686             this.onRotateFail();
27687             
27688             return false;
27689         }
27690         
27691         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27692
27693         if(this.isDocument){
27694             this.setThumbBoxSize();
27695             this.setThumbBoxPosition();
27696             this.setCanvasPosition();
27697         }
27698         
27699         this.draw();
27700         
27701         this.fireEvent('rotate', this, 'right');
27702     },
27703     
27704     onRotateFail : function()
27705     {
27706         this.errorEl.show(true);
27707         
27708         var _this = this;
27709         
27710         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27711     },
27712     
27713     draw : function()
27714     {
27715         this.previewEl.dom.innerHTML = '';
27716         
27717         var canvasEl = document.createElement("canvas");
27718         
27719         var contextEl = canvasEl.getContext("2d");
27720         
27721         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27722         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27723         var center = this.imageEl.OriginWidth / 2;
27724         
27725         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27726             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27727             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27728             center = this.imageEl.OriginHeight / 2;
27729         }
27730         
27731         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27732         
27733         contextEl.translate(center, center);
27734         contextEl.rotate(this.rotate * Math.PI / 180);
27735
27736         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27737         
27738         this.canvasEl = document.createElement("canvas");
27739         
27740         this.contextEl = this.canvasEl.getContext("2d");
27741         
27742         switch (this.rotate) {
27743             case 0 :
27744                 
27745                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27746                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27747                 
27748                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27749                 
27750                 break;
27751             case 90 : 
27752                 
27753                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27754                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27755                 
27756                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27757                     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);
27758                     break;
27759                 }
27760                 
27761                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27762                 
27763                 break;
27764             case 180 :
27765                 
27766                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27767                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27768                 
27769                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27770                     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);
27771                     break;
27772                 }
27773                 
27774                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27775                 
27776                 break;
27777             case 270 :
27778                 
27779                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27780                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27781         
27782                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27783                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27784                     break;
27785                 }
27786                 
27787                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27788                 
27789                 break;
27790             default : 
27791                 break;
27792         }
27793         
27794         this.previewEl.appendChild(this.canvasEl);
27795         
27796         this.setCanvasPosition();
27797     },
27798     
27799     crop : function()
27800     {
27801         if(!this.canvasLoaded){
27802             return;
27803         }
27804         
27805         var imageCanvas = document.createElement("canvas");
27806         
27807         var imageContext = imageCanvas.getContext("2d");
27808         
27809         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27810         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27811         
27812         var center = imageCanvas.width / 2;
27813         
27814         imageContext.translate(center, center);
27815         
27816         imageContext.rotate(this.rotate * Math.PI / 180);
27817         
27818         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27819         
27820         var canvas = document.createElement("canvas");
27821         
27822         var context = canvas.getContext("2d");
27823                 
27824         canvas.width = this.minWidth;
27825         canvas.height = this.minHeight;
27826
27827         switch (this.rotate) {
27828             case 0 :
27829                 
27830                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27831                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27832                 
27833                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27834                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27835                 
27836                 var targetWidth = this.minWidth - 2 * x;
27837                 var targetHeight = this.minHeight - 2 * y;
27838                 
27839                 var scale = 1;
27840                 
27841                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27842                     scale = targetWidth / width;
27843                 }
27844                 
27845                 if(x > 0 && y == 0){
27846                     scale = targetHeight / height;
27847                 }
27848                 
27849                 if(x > 0 && y > 0){
27850                     scale = targetWidth / width;
27851                     
27852                     if(width < height){
27853                         scale = targetHeight / height;
27854                     }
27855                 }
27856                 
27857                 context.scale(scale, scale);
27858                 
27859                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27860                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27861
27862                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27863                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27864
27865                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27866                 
27867                 break;
27868             case 90 : 
27869                 
27870                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27871                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27872                 
27873                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27874                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27875                 
27876                 var targetWidth = this.minWidth - 2 * x;
27877                 var targetHeight = this.minHeight - 2 * y;
27878                 
27879                 var scale = 1;
27880                 
27881                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27882                     scale = targetWidth / width;
27883                 }
27884                 
27885                 if(x > 0 && y == 0){
27886                     scale = targetHeight / height;
27887                 }
27888                 
27889                 if(x > 0 && y > 0){
27890                     scale = targetWidth / width;
27891                     
27892                     if(width < height){
27893                         scale = targetHeight / height;
27894                     }
27895                 }
27896                 
27897                 context.scale(scale, scale);
27898                 
27899                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27900                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27901
27902                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27903                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27904                 
27905                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27906                 
27907                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27908                 
27909                 break;
27910             case 180 :
27911                 
27912                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27913                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27914                 
27915                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27916                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27917                 
27918                 var targetWidth = this.minWidth - 2 * x;
27919                 var targetHeight = this.minHeight - 2 * y;
27920                 
27921                 var scale = 1;
27922                 
27923                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27924                     scale = targetWidth / width;
27925                 }
27926                 
27927                 if(x > 0 && y == 0){
27928                     scale = targetHeight / height;
27929                 }
27930                 
27931                 if(x > 0 && y > 0){
27932                     scale = targetWidth / width;
27933                     
27934                     if(width < height){
27935                         scale = targetHeight / height;
27936                     }
27937                 }
27938                 
27939                 context.scale(scale, scale);
27940                 
27941                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27942                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27943
27944                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27945                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27946
27947                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27948                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27949                 
27950                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27951                 
27952                 break;
27953             case 270 :
27954                 
27955                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27956                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27957                 
27958                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27959                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27960                 
27961                 var targetWidth = this.minWidth - 2 * x;
27962                 var targetHeight = this.minHeight - 2 * y;
27963                 
27964                 var scale = 1;
27965                 
27966                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27967                     scale = targetWidth / width;
27968                 }
27969                 
27970                 if(x > 0 && y == 0){
27971                     scale = targetHeight / height;
27972                 }
27973                 
27974                 if(x > 0 && y > 0){
27975                     scale = targetWidth / width;
27976                     
27977                     if(width < height){
27978                         scale = targetHeight / height;
27979                     }
27980                 }
27981                 
27982                 context.scale(scale, scale);
27983                 
27984                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27985                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27986
27987                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27988                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27989                 
27990                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27991                 
27992                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27993                 
27994                 break;
27995             default : 
27996                 break;
27997         }
27998         
27999         this.cropData = canvas.toDataURL(this.cropType);
28000         
28001         if(this.fireEvent('crop', this, this.cropData) !== false){
28002             this.process(this.file, this.cropData);
28003         }
28004         
28005         return;
28006         
28007     },
28008     
28009     setThumbBoxSize : function()
28010     {
28011         var width, height;
28012         
28013         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28014             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28015             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28016             
28017             this.minWidth = width;
28018             this.minHeight = height;
28019             
28020             if(this.rotate == 90 || this.rotate == 270){
28021                 this.minWidth = height;
28022                 this.minHeight = width;
28023             }
28024         }
28025         
28026         height = 300;
28027         width = Math.ceil(this.minWidth * height / this.minHeight);
28028         
28029         if(this.minWidth > this.minHeight){
28030             width = 300;
28031             height = Math.ceil(this.minHeight * width / this.minWidth);
28032         }
28033         
28034         this.thumbEl.setStyle({
28035             width : width + 'px',
28036             height : height + 'px'
28037         });
28038
28039         return;
28040             
28041     },
28042     
28043     setThumbBoxPosition : function()
28044     {
28045         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28046         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28047         
28048         this.thumbEl.setLeft(x);
28049         this.thumbEl.setTop(y);
28050         
28051     },
28052     
28053     baseRotateLevel : function()
28054     {
28055         this.baseRotate = 1;
28056         
28057         if(
28058                 typeof(this.exif) != 'undefined' &&
28059                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28060                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28061         ){
28062             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28063         }
28064         
28065         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28066         
28067     },
28068     
28069     baseScaleLevel : function()
28070     {
28071         var width, height;
28072         
28073         if(this.isDocument){
28074             
28075             if(this.baseRotate == 6 || this.baseRotate == 8){
28076             
28077                 height = this.thumbEl.getHeight();
28078                 this.baseScale = height / this.imageEl.OriginWidth;
28079
28080                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28081                     width = this.thumbEl.getWidth();
28082                     this.baseScale = width / this.imageEl.OriginHeight;
28083                 }
28084
28085                 return;
28086             }
28087
28088             height = this.thumbEl.getHeight();
28089             this.baseScale = height / this.imageEl.OriginHeight;
28090
28091             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28092                 width = this.thumbEl.getWidth();
28093                 this.baseScale = width / this.imageEl.OriginWidth;
28094             }
28095
28096             return;
28097         }
28098         
28099         if(this.baseRotate == 6 || this.baseRotate == 8){
28100             
28101             width = this.thumbEl.getHeight();
28102             this.baseScale = width / this.imageEl.OriginHeight;
28103             
28104             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28105                 height = this.thumbEl.getWidth();
28106                 this.baseScale = height / this.imageEl.OriginHeight;
28107             }
28108             
28109             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28110                 height = this.thumbEl.getWidth();
28111                 this.baseScale = height / this.imageEl.OriginHeight;
28112                 
28113                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28114                     width = this.thumbEl.getHeight();
28115                     this.baseScale = width / this.imageEl.OriginWidth;
28116                 }
28117             }
28118             
28119             return;
28120         }
28121         
28122         width = this.thumbEl.getWidth();
28123         this.baseScale = width / this.imageEl.OriginWidth;
28124         
28125         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28126             height = this.thumbEl.getHeight();
28127             this.baseScale = height / this.imageEl.OriginHeight;
28128         }
28129         
28130         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28131             
28132             height = this.thumbEl.getHeight();
28133             this.baseScale = height / this.imageEl.OriginHeight;
28134             
28135             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28136                 width = this.thumbEl.getWidth();
28137                 this.baseScale = width / this.imageEl.OriginWidth;
28138             }
28139             
28140         }
28141         
28142         return;
28143     },
28144     
28145     getScaleLevel : function()
28146     {
28147         return this.baseScale * Math.pow(1.1, this.scale);
28148     },
28149     
28150     onTouchStart : function(e)
28151     {
28152         if(!this.canvasLoaded){
28153             this.beforeSelectFile(e);
28154             return;
28155         }
28156         
28157         var touches = e.browserEvent.touches;
28158         
28159         if(!touches){
28160             return;
28161         }
28162         
28163         if(touches.length == 1){
28164             this.onMouseDown(e);
28165             return;
28166         }
28167         
28168         if(touches.length != 2){
28169             return;
28170         }
28171         
28172         var coords = [];
28173         
28174         for(var i = 0, finger; finger = touches[i]; i++){
28175             coords.push(finger.pageX, finger.pageY);
28176         }
28177         
28178         var x = Math.pow(coords[0] - coords[2], 2);
28179         var y = Math.pow(coords[1] - coords[3], 2);
28180         
28181         this.startDistance = Math.sqrt(x + y);
28182         
28183         this.startScale = this.scale;
28184         
28185         this.pinching = true;
28186         this.dragable = false;
28187         
28188     },
28189     
28190     onTouchMove : function(e)
28191     {
28192         if(!this.pinching && !this.dragable){
28193             return;
28194         }
28195         
28196         var touches = e.browserEvent.touches;
28197         
28198         if(!touches){
28199             return;
28200         }
28201         
28202         if(this.dragable){
28203             this.onMouseMove(e);
28204             return;
28205         }
28206         
28207         var coords = [];
28208         
28209         for(var i = 0, finger; finger = touches[i]; i++){
28210             coords.push(finger.pageX, finger.pageY);
28211         }
28212         
28213         var x = Math.pow(coords[0] - coords[2], 2);
28214         var y = Math.pow(coords[1] - coords[3], 2);
28215         
28216         this.endDistance = Math.sqrt(x + y);
28217         
28218         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28219         
28220         if(!this.zoomable()){
28221             this.scale = this.startScale;
28222             return;
28223         }
28224         
28225         this.draw();
28226         
28227     },
28228     
28229     onTouchEnd : function(e)
28230     {
28231         this.pinching = false;
28232         this.dragable = false;
28233         
28234     },
28235     
28236     process : function(file, crop)
28237     {
28238         if(this.loadMask){
28239             this.maskEl.mask(this.loadingText);
28240         }
28241         
28242         this.xhr = new XMLHttpRequest();
28243         
28244         file.xhr = this.xhr;
28245
28246         this.xhr.open(this.method, this.url, true);
28247         
28248         var headers = {
28249             "Accept": "application/json",
28250             "Cache-Control": "no-cache",
28251             "X-Requested-With": "XMLHttpRequest"
28252         };
28253         
28254         for (var headerName in headers) {
28255             var headerValue = headers[headerName];
28256             if (headerValue) {
28257                 this.xhr.setRequestHeader(headerName, headerValue);
28258             }
28259         }
28260         
28261         var _this = this;
28262         
28263         this.xhr.onload = function()
28264         {
28265             _this.xhrOnLoad(_this.xhr);
28266         }
28267         
28268         this.xhr.onerror = function()
28269         {
28270             _this.xhrOnError(_this.xhr);
28271         }
28272         
28273         var formData = new FormData();
28274
28275         formData.append('returnHTML', 'NO');
28276         
28277         if(crop){
28278             formData.append('crop', crop);
28279         }
28280         
28281         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28282             formData.append(this.paramName, file, file.name);
28283         }
28284         
28285         if(typeof(file.filename) != 'undefined'){
28286             formData.append('filename', file.filename);
28287         }
28288         
28289         if(typeof(file.mimetype) != 'undefined'){
28290             formData.append('mimetype', file.mimetype);
28291         }
28292         
28293         if(this.fireEvent('arrange', this, formData) != false){
28294             this.xhr.send(formData);
28295         };
28296     },
28297     
28298     xhrOnLoad : function(xhr)
28299     {
28300         if(this.loadMask){
28301             this.maskEl.unmask();
28302         }
28303         
28304         if (xhr.readyState !== 4) {
28305             this.fireEvent('exception', this, xhr);
28306             return;
28307         }
28308
28309         var response = Roo.decode(xhr.responseText);
28310         
28311         if(!response.success){
28312             this.fireEvent('exception', this, xhr);
28313             return;
28314         }
28315         
28316         var response = Roo.decode(xhr.responseText);
28317         
28318         this.fireEvent('upload', this, response);
28319         
28320     },
28321     
28322     xhrOnError : function()
28323     {
28324         if(this.loadMask){
28325             this.maskEl.unmask();
28326         }
28327         
28328         Roo.log('xhr on error');
28329         
28330         var response = Roo.decode(xhr.responseText);
28331           
28332         Roo.log(response);
28333         
28334     },
28335     
28336     prepare : function(file)
28337     {   
28338         if(this.loadMask){
28339             this.maskEl.mask(this.loadingText);
28340         }
28341         
28342         this.file = false;
28343         this.exif = {};
28344         
28345         if(typeof(file) === 'string'){
28346             this.loadCanvas(file);
28347             return;
28348         }
28349         
28350         if(!file || !this.urlAPI){
28351             return;
28352         }
28353         
28354         this.file = file;
28355         this.cropType = file.type;
28356         
28357         var _this = this;
28358         
28359         if(this.fireEvent('prepare', this, this.file) != false){
28360             
28361             var reader = new FileReader();
28362             
28363             reader.onload = function (e) {
28364                 if (e.target.error) {
28365                     Roo.log(e.target.error);
28366                     return;
28367                 }
28368                 
28369                 var buffer = e.target.result,
28370                     dataView = new DataView(buffer),
28371                     offset = 2,
28372                     maxOffset = dataView.byteLength - 4,
28373                     markerBytes,
28374                     markerLength;
28375                 
28376                 if (dataView.getUint16(0) === 0xffd8) {
28377                     while (offset < maxOffset) {
28378                         markerBytes = dataView.getUint16(offset);
28379                         
28380                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28381                             markerLength = dataView.getUint16(offset + 2) + 2;
28382                             if (offset + markerLength > dataView.byteLength) {
28383                                 Roo.log('Invalid meta data: Invalid segment size.');
28384                                 break;
28385                             }
28386                             
28387                             if(markerBytes == 0xffe1){
28388                                 _this.parseExifData(
28389                                     dataView,
28390                                     offset,
28391                                     markerLength
28392                                 );
28393                             }
28394                             
28395                             offset += markerLength;
28396                             
28397                             continue;
28398                         }
28399                         
28400                         break;
28401                     }
28402                     
28403                 }
28404                 
28405                 var url = _this.urlAPI.createObjectURL(_this.file);
28406                 
28407                 _this.loadCanvas(url);
28408                 
28409                 return;
28410             }
28411             
28412             reader.readAsArrayBuffer(this.file);
28413             
28414         }
28415         
28416     },
28417     
28418     parseExifData : function(dataView, offset, length)
28419     {
28420         var tiffOffset = offset + 10,
28421             littleEndian,
28422             dirOffset;
28423     
28424         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28425             // No Exif data, might be XMP data instead
28426             return;
28427         }
28428         
28429         // Check for the ASCII code for "Exif" (0x45786966):
28430         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28431             // No Exif data, might be XMP data instead
28432             return;
28433         }
28434         if (tiffOffset + 8 > dataView.byteLength) {
28435             Roo.log('Invalid Exif data: Invalid segment size.');
28436             return;
28437         }
28438         // Check for the two null bytes:
28439         if (dataView.getUint16(offset + 8) !== 0x0000) {
28440             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28441             return;
28442         }
28443         // Check the byte alignment:
28444         switch (dataView.getUint16(tiffOffset)) {
28445         case 0x4949:
28446             littleEndian = true;
28447             break;
28448         case 0x4D4D:
28449             littleEndian = false;
28450             break;
28451         default:
28452             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28453             return;
28454         }
28455         // Check for the TIFF tag marker (0x002A):
28456         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28457             Roo.log('Invalid Exif data: Missing TIFF marker.');
28458             return;
28459         }
28460         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28461         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28462         
28463         this.parseExifTags(
28464             dataView,
28465             tiffOffset,
28466             tiffOffset + dirOffset,
28467             littleEndian
28468         );
28469     },
28470     
28471     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28472     {
28473         var tagsNumber,
28474             dirEndOffset,
28475             i;
28476         if (dirOffset + 6 > dataView.byteLength) {
28477             Roo.log('Invalid Exif data: Invalid directory offset.');
28478             return;
28479         }
28480         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28481         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28482         if (dirEndOffset + 4 > dataView.byteLength) {
28483             Roo.log('Invalid Exif data: Invalid directory size.');
28484             return;
28485         }
28486         for (i = 0; i < tagsNumber; i += 1) {
28487             this.parseExifTag(
28488                 dataView,
28489                 tiffOffset,
28490                 dirOffset + 2 + 12 * i, // tag offset
28491                 littleEndian
28492             );
28493         }
28494         // Return the offset to the next directory:
28495         return dataView.getUint32(dirEndOffset, littleEndian);
28496     },
28497     
28498     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28499     {
28500         var tag = dataView.getUint16(offset, littleEndian);
28501         
28502         this.exif[tag] = this.getExifValue(
28503             dataView,
28504             tiffOffset,
28505             offset,
28506             dataView.getUint16(offset + 2, littleEndian), // tag type
28507             dataView.getUint32(offset + 4, littleEndian), // tag length
28508             littleEndian
28509         );
28510     },
28511     
28512     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28513     {
28514         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28515             tagSize,
28516             dataOffset,
28517             values,
28518             i,
28519             str,
28520             c;
28521     
28522         if (!tagType) {
28523             Roo.log('Invalid Exif data: Invalid tag type.');
28524             return;
28525         }
28526         
28527         tagSize = tagType.size * length;
28528         // Determine if the value is contained in the dataOffset bytes,
28529         // or if the value at the dataOffset is a pointer to the actual data:
28530         dataOffset = tagSize > 4 ?
28531                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28532         if (dataOffset + tagSize > dataView.byteLength) {
28533             Roo.log('Invalid Exif data: Invalid data offset.');
28534             return;
28535         }
28536         if (length === 1) {
28537             return tagType.getValue(dataView, dataOffset, littleEndian);
28538         }
28539         values = [];
28540         for (i = 0; i < length; i += 1) {
28541             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28542         }
28543         
28544         if (tagType.ascii) {
28545             str = '';
28546             // Concatenate the chars:
28547             for (i = 0; i < values.length; i += 1) {
28548                 c = values[i];
28549                 // Ignore the terminating NULL byte(s):
28550                 if (c === '\u0000') {
28551                     break;
28552                 }
28553                 str += c;
28554             }
28555             return str;
28556         }
28557         return values;
28558     }
28559     
28560 });
28561
28562 Roo.apply(Roo.bootstrap.UploadCropbox, {
28563     tags : {
28564         'Orientation': 0x0112
28565     },
28566     
28567     Orientation: {
28568             1: 0, //'top-left',
28569 //            2: 'top-right',
28570             3: 180, //'bottom-right',
28571 //            4: 'bottom-left',
28572 //            5: 'left-top',
28573             6: 90, //'right-top',
28574 //            7: 'right-bottom',
28575             8: 270 //'left-bottom'
28576     },
28577     
28578     exifTagTypes : {
28579         // byte, 8-bit unsigned int:
28580         1: {
28581             getValue: function (dataView, dataOffset) {
28582                 return dataView.getUint8(dataOffset);
28583             },
28584             size: 1
28585         },
28586         // ascii, 8-bit byte:
28587         2: {
28588             getValue: function (dataView, dataOffset) {
28589                 return String.fromCharCode(dataView.getUint8(dataOffset));
28590             },
28591             size: 1,
28592             ascii: true
28593         },
28594         // short, 16 bit int:
28595         3: {
28596             getValue: function (dataView, dataOffset, littleEndian) {
28597                 return dataView.getUint16(dataOffset, littleEndian);
28598             },
28599             size: 2
28600         },
28601         // long, 32 bit int:
28602         4: {
28603             getValue: function (dataView, dataOffset, littleEndian) {
28604                 return dataView.getUint32(dataOffset, littleEndian);
28605             },
28606             size: 4
28607         },
28608         // rational = two long values, first is numerator, second is denominator:
28609         5: {
28610             getValue: function (dataView, dataOffset, littleEndian) {
28611                 return dataView.getUint32(dataOffset, littleEndian) /
28612                     dataView.getUint32(dataOffset + 4, littleEndian);
28613             },
28614             size: 8
28615         },
28616         // slong, 32 bit signed int:
28617         9: {
28618             getValue: function (dataView, dataOffset, littleEndian) {
28619                 return dataView.getInt32(dataOffset, littleEndian);
28620             },
28621             size: 4
28622         },
28623         // srational, two slongs, first is numerator, second is denominator:
28624         10: {
28625             getValue: function (dataView, dataOffset, littleEndian) {
28626                 return dataView.getInt32(dataOffset, littleEndian) /
28627                     dataView.getInt32(dataOffset + 4, littleEndian);
28628             },
28629             size: 8
28630         }
28631     },
28632     
28633     footer : {
28634         STANDARD : [
28635             {
28636                 tag : 'div',
28637                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28638                 action : 'rotate-left',
28639                 cn : [
28640                     {
28641                         tag : 'button',
28642                         cls : 'btn btn-default',
28643                         html : '<i class="fa fa-undo"></i>'
28644                     }
28645                 ]
28646             },
28647             {
28648                 tag : 'div',
28649                 cls : 'btn-group roo-upload-cropbox-picture',
28650                 action : 'picture',
28651                 cn : [
28652                     {
28653                         tag : 'button',
28654                         cls : 'btn btn-default',
28655                         html : '<i class="fa fa-picture-o"></i>'
28656                     }
28657                 ]
28658             },
28659             {
28660                 tag : 'div',
28661                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28662                 action : 'rotate-right',
28663                 cn : [
28664                     {
28665                         tag : 'button',
28666                         cls : 'btn btn-default',
28667                         html : '<i class="fa fa-repeat"></i>'
28668                     }
28669                 ]
28670             }
28671         ],
28672         DOCUMENT : [
28673             {
28674                 tag : 'div',
28675                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28676                 action : 'rotate-left',
28677                 cn : [
28678                     {
28679                         tag : 'button',
28680                         cls : 'btn btn-default',
28681                         html : '<i class="fa fa-undo"></i>'
28682                     }
28683                 ]
28684             },
28685             {
28686                 tag : 'div',
28687                 cls : 'btn-group roo-upload-cropbox-download',
28688                 action : 'download',
28689                 cn : [
28690                     {
28691                         tag : 'button',
28692                         cls : 'btn btn-default',
28693                         html : '<i class="fa fa-download"></i>'
28694                     }
28695                 ]
28696             },
28697             {
28698                 tag : 'div',
28699                 cls : 'btn-group roo-upload-cropbox-crop',
28700                 action : 'crop',
28701                 cn : [
28702                     {
28703                         tag : 'button',
28704                         cls : 'btn btn-default',
28705                         html : '<i class="fa fa-crop"></i>'
28706                     }
28707                 ]
28708             },
28709             {
28710                 tag : 'div',
28711                 cls : 'btn-group roo-upload-cropbox-trash',
28712                 action : 'trash',
28713                 cn : [
28714                     {
28715                         tag : 'button',
28716                         cls : 'btn btn-default',
28717                         html : '<i class="fa fa-trash"></i>'
28718                     }
28719                 ]
28720             },
28721             {
28722                 tag : 'div',
28723                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28724                 action : 'rotate-right',
28725                 cn : [
28726                     {
28727                         tag : 'button',
28728                         cls : 'btn btn-default',
28729                         html : '<i class="fa fa-repeat"></i>'
28730                     }
28731                 ]
28732             }
28733         ],
28734         ROTATOR : [
28735             {
28736                 tag : 'div',
28737                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28738                 action : 'rotate-left',
28739                 cn : [
28740                     {
28741                         tag : 'button',
28742                         cls : 'btn btn-default',
28743                         html : '<i class="fa fa-undo"></i>'
28744                     }
28745                 ]
28746             },
28747             {
28748                 tag : 'div',
28749                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28750                 action : 'rotate-right',
28751                 cn : [
28752                     {
28753                         tag : 'button',
28754                         cls : 'btn btn-default',
28755                         html : '<i class="fa fa-repeat"></i>'
28756                     }
28757                 ]
28758             }
28759         ]
28760     }
28761 });
28762
28763 /*
28764 * Licence: LGPL
28765 */
28766
28767 /**
28768  * @class Roo.bootstrap.DocumentManager
28769  * @extends Roo.bootstrap.Component
28770  * Bootstrap DocumentManager class
28771  * @cfg {String} paramName default 'imageUpload'
28772  * @cfg {String} toolTipName default 'filename'
28773  * @cfg {String} method default POST
28774  * @cfg {String} url action url
28775  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28776  * @cfg {Boolean} multiple multiple upload default true
28777  * @cfg {Number} thumbSize default 300
28778  * @cfg {String} fieldLabel
28779  * @cfg {Number} labelWidth default 4
28780  * @cfg {String} labelAlign (left|top) default left
28781  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28782 * @cfg {Number} labellg set the width of label (1-12)
28783  * @cfg {Number} labelmd set the width of label (1-12)
28784  * @cfg {Number} labelsm set the width of label (1-12)
28785  * @cfg {Number} labelxs set the width of label (1-12)
28786  * 
28787  * @constructor
28788  * Create a new DocumentManager
28789  * @param {Object} config The config object
28790  */
28791
28792 Roo.bootstrap.DocumentManager = function(config){
28793     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28794     
28795     this.files = [];
28796     this.delegates = [];
28797     
28798     this.addEvents({
28799         /**
28800          * @event initial
28801          * Fire when initial the DocumentManager
28802          * @param {Roo.bootstrap.DocumentManager} this
28803          */
28804         "initial" : true,
28805         /**
28806          * @event inspect
28807          * inspect selected file
28808          * @param {Roo.bootstrap.DocumentManager} this
28809          * @param {File} file
28810          */
28811         "inspect" : true,
28812         /**
28813          * @event exception
28814          * Fire when xhr load exception
28815          * @param {Roo.bootstrap.DocumentManager} this
28816          * @param {XMLHttpRequest} xhr
28817          */
28818         "exception" : true,
28819         /**
28820          * @event afterupload
28821          * Fire when xhr load exception
28822          * @param {Roo.bootstrap.DocumentManager} this
28823          * @param {XMLHttpRequest} xhr
28824          */
28825         "afterupload" : true,
28826         /**
28827          * @event prepare
28828          * prepare the form data
28829          * @param {Roo.bootstrap.DocumentManager} this
28830          * @param {Object} formData
28831          */
28832         "prepare" : true,
28833         /**
28834          * @event remove
28835          * Fire when remove the file
28836          * @param {Roo.bootstrap.DocumentManager} this
28837          * @param {Object} file
28838          */
28839         "remove" : true,
28840         /**
28841          * @event refresh
28842          * Fire after refresh the file
28843          * @param {Roo.bootstrap.DocumentManager} this
28844          */
28845         "refresh" : true,
28846         /**
28847          * @event click
28848          * Fire after click the image
28849          * @param {Roo.bootstrap.DocumentManager} this
28850          * @param {Object} file
28851          */
28852         "click" : true,
28853         /**
28854          * @event edit
28855          * Fire when upload a image and editable set to true
28856          * @param {Roo.bootstrap.DocumentManager} this
28857          * @param {Object} file
28858          */
28859         "edit" : true,
28860         /**
28861          * @event beforeselectfile
28862          * Fire before select file
28863          * @param {Roo.bootstrap.DocumentManager} this
28864          */
28865         "beforeselectfile" : true,
28866         /**
28867          * @event process
28868          * Fire before process file
28869          * @param {Roo.bootstrap.DocumentManager} this
28870          * @param {Object} file
28871          */
28872         "process" : true,
28873         /**
28874          * @event previewrendered
28875          * Fire when preview rendered
28876          * @param {Roo.bootstrap.DocumentManager} this
28877          * @param {Object} file
28878          */
28879         "previewrendered" : true,
28880         /**
28881          */
28882         "previewResize" : true
28883         
28884     });
28885 };
28886
28887 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28888     
28889     boxes : 0,
28890     inputName : '',
28891     thumbSize : 300,
28892     multiple : true,
28893     files : false,
28894     method : 'POST',
28895     url : '',
28896     paramName : 'imageUpload',
28897     toolTipName : 'filename',
28898     fieldLabel : '',
28899     labelWidth : 4,
28900     labelAlign : 'left',
28901     editable : true,
28902     delegates : false,
28903     xhr : false, 
28904     
28905     labellg : 0,
28906     labelmd : 0,
28907     labelsm : 0,
28908     labelxs : 0,
28909     
28910     getAutoCreate : function()
28911     {   
28912         var managerWidget = {
28913             tag : 'div',
28914             cls : 'roo-document-manager',
28915             cn : [
28916                 {
28917                     tag : 'input',
28918                     cls : 'roo-document-manager-selector',
28919                     type : 'file'
28920                 },
28921                 {
28922                     tag : 'div',
28923                     cls : 'roo-document-manager-uploader',
28924                     cn : [
28925                         {
28926                             tag : 'div',
28927                             cls : 'roo-document-manager-upload-btn',
28928                             html : '<i class="fa fa-plus"></i>'
28929                         }
28930                     ]
28931                     
28932                 }
28933             ]
28934         };
28935         
28936         var content = [
28937             {
28938                 tag : 'div',
28939                 cls : 'column col-md-12',
28940                 cn : managerWidget
28941             }
28942         ];
28943         
28944         if(this.fieldLabel.length){
28945             
28946             content = [
28947                 {
28948                     tag : 'div',
28949                     cls : 'column col-md-12',
28950                     html : this.fieldLabel
28951                 },
28952                 {
28953                     tag : 'div',
28954                     cls : 'column col-md-12',
28955                     cn : managerWidget
28956                 }
28957             ];
28958
28959             if(this.labelAlign == 'left'){
28960                 content = [
28961                     {
28962                         tag : 'div',
28963                         cls : 'column',
28964                         html : this.fieldLabel
28965                     },
28966                     {
28967                         tag : 'div',
28968                         cls : 'column',
28969                         cn : managerWidget
28970                     }
28971                 ];
28972                 
28973                 if(this.labelWidth > 12){
28974                     content[0].style = "width: " + this.labelWidth + 'px';
28975                 }
28976
28977                 if(this.labelWidth < 13 && this.labelmd == 0){
28978                     this.labelmd = this.labelWidth;
28979                 }
28980
28981                 if(this.labellg > 0){
28982                     content[0].cls += ' col-lg-' + this.labellg;
28983                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28984                 }
28985
28986                 if(this.labelmd > 0){
28987                     content[0].cls += ' col-md-' + this.labelmd;
28988                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28989                 }
28990
28991                 if(this.labelsm > 0){
28992                     content[0].cls += ' col-sm-' + this.labelsm;
28993                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28994                 }
28995
28996                 if(this.labelxs > 0){
28997                     content[0].cls += ' col-xs-' + this.labelxs;
28998                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28999                 }
29000                 
29001             }
29002         }
29003         
29004         var cfg = {
29005             tag : 'div',
29006             cls : 'row clearfix',
29007             cn : content
29008         };
29009         
29010         return cfg;
29011         
29012     },
29013     
29014     initEvents : function()
29015     {
29016         this.managerEl = this.el.select('.roo-document-manager', true).first();
29017         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29018         
29019         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29020         this.selectorEl.hide();
29021         
29022         if(this.multiple){
29023             this.selectorEl.attr('multiple', 'multiple');
29024         }
29025         
29026         this.selectorEl.on('change', this.onFileSelected, this);
29027         
29028         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29029         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29030         
29031         this.uploader.on('click', this.onUploaderClick, this);
29032         
29033         this.renderProgressDialog();
29034         
29035         var _this = this;
29036         
29037         window.addEventListener("resize", function() { _this.refresh(); } );
29038         
29039         this.fireEvent('initial', this);
29040     },
29041     
29042     renderProgressDialog : function()
29043     {
29044         var _this = this;
29045         
29046         this.progressDialog = new Roo.bootstrap.Modal({
29047             cls : 'roo-document-manager-progress-dialog',
29048             allow_close : false,
29049             title : '',
29050             buttons : [
29051                 {
29052                     name  :'cancel',
29053                     weight : 'danger',
29054                     html : 'Cancel'
29055                 }
29056             ], 
29057             listeners : { 
29058                 btnclick : function() {
29059                     _this.uploadCancel();
29060                     this.hide();
29061                 }
29062             }
29063         });
29064          
29065         this.progressDialog.render(Roo.get(document.body));
29066          
29067         this.progress = new Roo.bootstrap.Progress({
29068             cls : 'roo-document-manager-progress',
29069             active : true,
29070             striped : true
29071         });
29072         
29073         this.progress.render(this.progressDialog.getChildContainer());
29074         
29075         this.progressBar = new Roo.bootstrap.ProgressBar({
29076             cls : 'roo-document-manager-progress-bar',
29077             aria_valuenow : 0,
29078             aria_valuemin : 0,
29079             aria_valuemax : 12,
29080             panel : 'success'
29081         });
29082         
29083         this.progressBar.render(this.progress.getChildContainer());
29084     },
29085     
29086     onUploaderClick : function(e)
29087     {
29088         e.preventDefault();
29089      
29090         if(this.fireEvent('beforeselectfile', this) != false){
29091             this.selectorEl.dom.click();
29092         }
29093         
29094     },
29095     
29096     onFileSelected : function(e)
29097     {
29098         e.preventDefault();
29099         
29100         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29101             return;
29102         }
29103         
29104         Roo.each(this.selectorEl.dom.files, function(file){
29105             if(this.fireEvent('inspect', this, file) != false){
29106                 this.files.push(file);
29107             }
29108         }, this);
29109         
29110         this.queue();
29111         
29112     },
29113     
29114     queue : function()
29115     {
29116         this.selectorEl.dom.value = '';
29117         
29118         if(!this.files || !this.files.length){
29119             return;
29120         }
29121         
29122         if(this.boxes > 0 && this.files.length > this.boxes){
29123             this.files = this.files.slice(0, this.boxes);
29124         }
29125         
29126         this.uploader.show();
29127         
29128         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29129             this.uploader.hide();
29130         }
29131         
29132         var _this = this;
29133         
29134         var files = [];
29135         
29136         var docs = [];
29137         
29138         Roo.each(this.files, function(file){
29139             
29140             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29141                 var f = this.renderPreview(file);
29142                 files.push(f);
29143                 return;
29144             }
29145             
29146             if(file.type.indexOf('image') != -1){
29147                 this.delegates.push(
29148                     (function(){
29149                         _this.process(file);
29150                     }).createDelegate(this)
29151                 );
29152         
29153                 return;
29154             }
29155             
29156             docs.push(
29157                 (function(){
29158                     _this.process(file);
29159                 }).createDelegate(this)
29160             );
29161             
29162         }, this);
29163         
29164         this.files = files;
29165         
29166         this.delegates = this.delegates.concat(docs);
29167         
29168         if(!this.delegates.length){
29169             this.refresh();
29170             return;
29171         }
29172         
29173         this.progressBar.aria_valuemax = this.delegates.length;
29174         
29175         this.arrange();
29176         
29177         return;
29178     },
29179     
29180     arrange : function()
29181     {
29182         if(!this.delegates.length){
29183             this.progressDialog.hide();
29184             this.refresh();
29185             return;
29186         }
29187         
29188         var delegate = this.delegates.shift();
29189         
29190         this.progressDialog.show();
29191         
29192         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29193         
29194         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29195         
29196         delegate();
29197     },
29198     
29199     refresh : function()
29200     {
29201         this.uploader.show();
29202         
29203         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29204             this.uploader.hide();
29205         }
29206         
29207         Roo.isTouch ? this.closable(false) : this.closable(true);
29208         
29209         this.fireEvent('refresh', this);
29210     },
29211     
29212     onRemove : function(e, el, o)
29213     {
29214         e.preventDefault();
29215         
29216         this.fireEvent('remove', this, o);
29217         
29218     },
29219     
29220     remove : function(o)
29221     {
29222         var files = [];
29223         
29224         Roo.each(this.files, function(file){
29225             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29226                 files.push(file);
29227                 return;
29228             }
29229
29230             o.target.remove();
29231
29232         }, this);
29233         
29234         this.files = files;
29235         
29236         this.refresh();
29237     },
29238     
29239     clear : function()
29240     {
29241         Roo.each(this.files, function(file){
29242             if(!file.target){
29243                 return;
29244             }
29245             
29246             file.target.remove();
29247
29248         }, this);
29249         
29250         this.files = [];
29251         
29252         this.refresh();
29253     },
29254     
29255     onClick : function(e, el, o)
29256     {
29257         e.preventDefault();
29258         
29259         this.fireEvent('click', this, o);
29260         
29261     },
29262     
29263     closable : function(closable)
29264     {
29265         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29266             
29267             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29268             
29269             if(closable){
29270                 el.show();
29271                 return;
29272             }
29273             
29274             el.hide();
29275             
29276         }, this);
29277     },
29278     
29279     xhrOnLoad : function(xhr)
29280     {
29281         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29282             el.remove();
29283         }, this);
29284         
29285         if (xhr.readyState !== 4) {
29286             this.arrange();
29287             this.fireEvent('exception', this, xhr);
29288             return;
29289         }
29290
29291         var response = Roo.decode(xhr.responseText);
29292         
29293         if(!response.success){
29294             this.arrange();
29295             this.fireEvent('exception', this, xhr);
29296             return;
29297         }
29298         
29299         var file = this.renderPreview(response.data);
29300         
29301         this.files.push(file);
29302         
29303         this.arrange();
29304         
29305         this.fireEvent('afterupload', this, xhr);
29306         
29307     },
29308     
29309     xhrOnError : function(xhr)
29310     {
29311         Roo.log('xhr on error');
29312         
29313         var response = Roo.decode(xhr.responseText);
29314           
29315         Roo.log(response);
29316         
29317         this.arrange();
29318     },
29319     
29320     process : function(file)
29321     {
29322         if(this.fireEvent('process', this, file) !== false){
29323             if(this.editable && file.type.indexOf('image') != -1){
29324                 this.fireEvent('edit', this, file);
29325                 return;
29326             }
29327
29328             this.uploadStart(file, false);
29329
29330             return;
29331         }
29332         
29333     },
29334     
29335     uploadStart : function(file, crop)
29336     {
29337         this.xhr = new XMLHttpRequest();
29338         
29339         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29340             this.arrange();
29341             return;
29342         }
29343         
29344         file.xhr = this.xhr;
29345             
29346         this.managerEl.createChild({
29347             tag : 'div',
29348             cls : 'roo-document-manager-loading',
29349             cn : [
29350                 {
29351                     tag : 'div',
29352                     tooltip : file.name,
29353                     cls : 'roo-document-manager-thumb',
29354                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29355                 }
29356             ]
29357
29358         });
29359
29360         this.xhr.open(this.method, this.url, true);
29361         
29362         var headers = {
29363             "Accept": "application/json",
29364             "Cache-Control": "no-cache",
29365             "X-Requested-With": "XMLHttpRequest"
29366         };
29367         
29368         for (var headerName in headers) {
29369             var headerValue = headers[headerName];
29370             if (headerValue) {
29371                 this.xhr.setRequestHeader(headerName, headerValue);
29372             }
29373         }
29374         
29375         var _this = this;
29376         
29377         this.xhr.onload = function()
29378         {
29379             _this.xhrOnLoad(_this.xhr);
29380         }
29381         
29382         this.xhr.onerror = function()
29383         {
29384             _this.xhrOnError(_this.xhr);
29385         }
29386         
29387         var formData = new FormData();
29388
29389         formData.append('returnHTML', 'NO');
29390         
29391         if(crop){
29392             formData.append('crop', crop);
29393         }
29394         
29395         formData.append(this.paramName, file, file.name);
29396         
29397         var options = {
29398             file : file, 
29399             manually : false
29400         };
29401         
29402         if(this.fireEvent('prepare', this, formData, options) != false){
29403             
29404             if(options.manually){
29405                 return;
29406             }
29407             
29408             this.xhr.send(formData);
29409             return;
29410         };
29411         
29412         this.uploadCancel();
29413     },
29414     
29415     uploadCancel : function()
29416     {
29417         if (this.xhr) {
29418             this.xhr.abort();
29419         }
29420         
29421         this.delegates = [];
29422         
29423         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29424             el.remove();
29425         }, this);
29426         
29427         this.arrange();
29428     },
29429     
29430     renderPreview : function(file)
29431     {
29432         if(typeof(file.target) != 'undefined' && file.target){
29433             return file;
29434         }
29435         
29436         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29437         
29438         var previewEl = this.managerEl.createChild({
29439             tag : 'div',
29440             cls : 'roo-document-manager-preview',
29441             cn : [
29442                 {
29443                     tag : 'div',
29444                     tooltip : file[this.toolTipName],
29445                     cls : 'roo-document-manager-thumb',
29446                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29447                 },
29448                 {
29449                     tag : 'button',
29450                     cls : 'close',
29451                     html : '<i class="fa fa-times-circle"></i>'
29452                 }
29453             ]
29454         });
29455
29456         var close = previewEl.select('button.close', true).first();
29457
29458         close.on('click', this.onRemove, this, file);
29459
29460         file.target = previewEl;
29461
29462         var image = previewEl.select('img', true).first();
29463         
29464         var _this = this;
29465         
29466         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29467         
29468         image.on('click', this.onClick, this, file);
29469         
29470         this.fireEvent('previewrendered', this, file);
29471         
29472         return file;
29473         
29474     },
29475     
29476     onPreviewLoad : function(file, image)
29477     {
29478         if(typeof(file.target) == 'undefined' || !file.target){
29479             return;
29480         }
29481         
29482         var width = image.dom.naturalWidth || image.dom.width;
29483         var height = image.dom.naturalHeight || image.dom.height;
29484         
29485         if(!this.previewResize) {
29486             return;
29487         }
29488         
29489         if(width > height){
29490             file.target.addClass('wide');
29491             return;
29492         }
29493         
29494         file.target.addClass('tall');
29495         return;
29496         
29497     },
29498     
29499     uploadFromSource : function(file, crop)
29500     {
29501         this.xhr = new XMLHttpRequest();
29502         
29503         this.managerEl.createChild({
29504             tag : 'div',
29505             cls : 'roo-document-manager-loading',
29506             cn : [
29507                 {
29508                     tag : 'div',
29509                     tooltip : file.name,
29510                     cls : 'roo-document-manager-thumb',
29511                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29512                 }
29513             ]
29514
29515         });
29516
29517         this.xhr.open(this.method, this.url, true);
29518         
29519         var headers = {
29520             "Accept": "application/json",
29521             "Cache-Control": "no-cache",
29522             "X-Requested-With": "XMLHttpRequest"
29523         };
29524         
29525         for (var headerName in headers) {
29526             var headerValue = headers[headerName];
29527             if (headerValue) {
29528                 this.xhr.setRequestHeader(headerName, headerValue);
29529             }
29530         }
29531         
29532         var _this = this;
29533         
29534         this.xhr.onload = function()
29535         {
29536             _this.xhrOnLoad(_this.xhr);
29537         }
29538         
29539         this.xhr.onerror = function()
29540         {
29541             _this.xhrOnError(_this.xhr);
29542         }
29543         
29544         var formData = new FormData();
29545
29546         formData.append('returnHTML', 'NO');
29547         
29548         formData.append('crop', crop);
29549         
29550         if(typeof(file.filename) != 'undefined'){
29551             formData.append('filename', file.filename);
29552         }
29553         
29554         if(typeof(file.mimetype) != 'undefined'){
29555             formData.append('mimetype', file.mimetype);
29556         }
29557         
29558         Roo.log(formData);
29559         
29560         if(this.fireEvent('prepare', this, formData) != false){
29561             this.xhr.send(formData);
29562         };
29563     }
29564 });
29565
29566 /*
29567 * Licence: LGPL
29568 */
29569
29570 /**
29571  * @class Roo.bootstrap.DocumentViewer
29572  * @extends Roo.bootstrap.Component
29573  * Bootstrap DocumentViewer class
29574  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29575  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29576  * 
29577  * @constructor
29578  * Create a new DocumentViewer
29579  * @param {Object} config The config object
29580  */
29581
29582 Roo.bootstrap.DocumentViewer = function(config){
29583     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29584     
29585     this.addEvents({
29586         /**
29587          * @event initial
29588          * Fire after initEvent
29589          * @param {Roo.bootstrap.DocumentViewer} this
29590          */
29591         "initial" : true,
29592         /**
29593          * @event click
29594          * Fire after click
29595          * @param {Roo.bootstrap.DocumentViewer} this
29596          */
29597         "click" : true,
29598         /**
29599          * @event download
29600          * Fire after download button
29601          * @param {Roo.bootstrap.DocumentViewer} this
29602          */
29603         "download" : true,
29604         /**
29605          * @event trash
29606          * Fire after trash button
29607          * @param {Roo.bootstrap.DocumentViewer} this
29608          */
29609         "trash" : true
29610         
29611     });
29612 };
29613
29614 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29615     
29616     showDownload : true,
29617     
29618     showTrash : true,
29619     
29620     getAutoCreate : function()
29621     {
29622         var cfg = {
29623             tag : 'div',
29624             cls : 'roo-document-viewer',
29625             cn : [
29626                 {
29627                     tag : 'div',
29628                     cls : 'roo-document-viewer-body',
29629                     cn : [
29630                         {
29631                             tag : 'div',
29632                             cls : 'roo-document-viewer-thumb',
29633                             cn : [
29634                                 {
29635                                     tag : 'img',
29636                                     cls : 'roo-document-viewer-image'
29637                                 }
29638                             ]
29639                         }
29640                     ]
29641                 },
29642                 {
29643                     tag : 'div',
29644                     cls : 'roo-document-viewer-footer',
29645                     cn : {
29646                         tag : 'div',
29647                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29648                         cn : [
29649                             {
29650                                 tag : 'div',
29651                                 cls : 'btn-group roo-document-viewer-download',
29652                                 cn : [
29653                                     {
29654                                         tag : 'button',
29655                                         cls : 'btn btn-default',
29656                                         html : '<i class="fa fa-download"></i>'
29657                                     }
29658                                 ]
29659                             },
29660                             {
29661                                 tag : 'div',
29662                                 cls : 'btn-group roo-document-viewer-trash',
29663                                 cn : [
29664                                     {
29665                                         tag : 'button',
29666                                         cls : 'btn btn-default',
29667                                         html : '<i class="fa fa-trash"></i>'
29668                                     }
29669                                 ]
29670                             }
29671                         ]
29672                     }
29673                 }
29674             ]
29675         };
29676         
29677         return cfg;
29678     },
29679     
29680     initEvents : function()
29681     {
29682         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29683         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29684         
29685         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29686         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29687         
29688         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29689         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29690         
29691         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29692         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29693         
29694         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29695         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29696         
29697         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29698         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29699         
29700         this.bodyEl.on('click', this.onClick, this);
29701         this.downloadBtn.on('click', this.onDownload, this);
29702         this.trashBtn.on('click', this.onTrash, this);
29703         
29704         this.downloadBtn.hide();
29705         this.trashBtn.hide();
29706         
29707         if(this.showDownload){
29708             this.downloadBtn.show();
29709         }
29710         
29711         if(this.showTrash){
29712             this.trashBtn.show();
29713         }
29714         
29715         if(!this.showDownload && !this.showTrash) {
29716             this.footerEl.hide();
29717         }
29718         
29719     },
29720     
29721     initial : function()
29722     {
29723         this.fireEvent('initial', this);
29724         
29725     },
29726     
29727     onClick : function(e)
29728     {
29729         e.preventDefault();
29730         
29731         this.fireEvent('click', this);
29732     },
29733     
29734     onDownload : function(e)
29735     {
29736         e.preventDefault();
29737         
29738         this.fireEvent('download', this);
29739     },
29740     
29741     onTrash : function(e)
29742     {
29743         e.preventDefault();
29744         
29745         this.fireEvent('trash', this);
29746     }
29747     
29748 });
29749 /*
29750  * - LGPL
29751  *
29752  * nav progress bar
29753  * 
29754  */
29755
29756 /**
29757  * @class Roo.bootstrap.NavProgressBar
29758  * @extends Roo.bootstrap.Component
29759  * Bootstrap NavProgressBar class
29760  * 
29761  * @constructor
29762  * Create a new nav progress bar
29763  * @param {Object} config The config object
29764  */
29765
29766 Roo.bootstrap.NavProgressBar = function(config){
29767     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29768
29769     this.bullets = this.bullets || [];
29770    
29771 //    Roo.bootstrap.NavProgressBar.register(this);
29772      this.addEvents({
29773         /**
29774              * @event changed
29775              * Fires when the active item changes
29776              * @param {Roo.bootstrap.NavProgressBar} this
29777              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29778              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29779          */
29780         'changed': true
29781      });
29782     
29783 };
29784
29785 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29786     
29787     bullets : [],
29788     barItems : [],
29789     
29790     getAutoCreate : function()
29791     {
29792         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29793         
29794         cfg = {
29795             tag : 'div',
29796             cls : 'roo-navigation-bar-group',
29797             cn : [
29798                 {
29799                     tag : 'div',
29800                     cls : 'roo-navigation-top-bar'
29801                 },
29802                 {
29803                     tag : 'div',
29804                     cls : 'roo-navigation-bullets-bar',
29805                     cn : [
29806                         {
29807                             tag : 'ul',
29808                             cls : 'roo-navigation-bar'
29809                         }
29810                     ]
29811                 },
29812                 
29813                 {
29814                     tag : 'div',
29815                     cls : 'roo-navigation-bottom-bar'
29816                 }
29817             ]
29818             
29819         };
29820         
29821         return cfg;
29822         
29823     },
29824     
29825     initEvents: function() 
29826     {
29827         
29828     },
29829     
29830     onRender : function(ct, position) 
29831     {
29832         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29833         
29834         if(this.bullets.length){
29835             Roo.each(this.bullets, function(b){
29836                this.addItem(b);
29837             }, this);
29838         }
29839         
29840         this.format();
29841         
29842     },
29843     
29844     addItem : function(cfg)
29845     {
29846         var item = new Roo.bootstrap.NavProgressItem(cfg);
29847         
29848         item.parentId = this.id;
29849         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29850         
29851         if(cfg.html){
29852             var top = new Roo.bootstrap.Element({
29853                 tag : 'div',
29854                 cls : 'roo-navigation-bar-text'
29855             });
29856             
29857             var bottom = new Roo.bootstrap.Element({
29858                 tag : 'div',
29859                 cls : 'roo-navigation-bar-text'
29860             });
29861             
29862             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29863             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29864             
29865             var topText = new Roo.bootstrap.Element({
29866                 tag : 'span',
29867                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29868             });
29869             
29870             var bottomText = new Roo.bootstrap.Element({
29871                 tag : 'span',
29872                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29873             });
29874             
29875             topText.onRender(top.el, null);
29876             bottomText.onRender(bottom.el, null);
29877             
29878             item.topEl = top;
29879             item.bottomEl = bottom;
29880         }
29881         
29882         this.barItems.push(item);
29883         
29884         return item;
29885     },
29886     
29887     getActive : function()
29888     {
29889         var active = false;
29890         
29891         Roo.each(this.barItems, function(v){
29892             
29893             if (!v.isActive()) {
29894                 return;
29895             }
29896             
29897             active = v;
29898             return false;
29899             
29900         });
29901         
29902         return active;
29903     },
29904     
29905     setActiveItem : function(item)
29906     {
29907         var prev = false;
29908         
29909         Roo.each(this.barItems, function(v){
29910             if (v.rid == item.rid) {
29911                 return ;
29912             }
29913             
29914             if (v.isActive()) {
29915                 v.setActive(false);
29916                 prev = v;
29917             }
29918         });
29919
29920         item.setActive(true);
29921         
29922         this.fireEvent('changed', this, item, prev);
29923     },
29924     
29925     getBarItem: function(rid)
29926     {
29927         var ret = false;
29928         
29929         Roo.each(this.barItems, function(e) {
29930             if (e.rid != rid) {
29931                 return;
29932             }
29933             
29934             ret =  e;
29935             return false;
29936         });
29937         
29938         return ret;
29939     },
29940     
29941     indexOfItem : function(item)
29942     {
29943         var index = false;
29944         
29945         Roo.each(this.barItems, function(v, i){
29946             
29947             if (v.rid != item.rid) {
29948                 return;
29949             }
29950             
29951             index = i;
29952             return false
29953         });
29954         
29955         return index;
29956     },
29957     
29958     setActiveNext : function()
29959     {
29960         var i = this.indexOfItem(this.getActive());
29961         
29962         if (i > this.barItems.length) {
29963             return;
29964         }
29965         
29966         this.setActiveItem(this.barItems[i+1]);
29967     },
29968     
29969     setActivePrev : function()
29970     {
29971         var i = this.indexOfItem(this.getActive());
29972         
29973         if (i  < 1) {
29974             return;
29975         }
29976         
29977         this.setActiveItem(this.barItems[i-1]);
29978     },
29979     
29980     format : function()
29981     {
29982         if(!this.barItems.length){
29983             return;
29984         }
29985      
29986         var width = 100 / this.barItems.length;
29987         
29988         Roo.each(this.barItems, function(i){
29989             i.el.setStyle('width', width + '%');
29990             i.topEl.el.setStyle('width', width + '%');
29991             i.bottomEl.el.setStyle('width', width + '%');
29992         }, this);
29993         
29994     }
29995     
29996 });
29997 /*
29998  * - LGPL
29999  *
30000  * Nav Progress Item
30001  * 
30002  */
30003
30004 /**
30005  * @class Roo.bootstrap.NavProgressItem
30006  * @extends Roo.bootstrap.Component
30007  * Bootstrap NavProgressItem class
30008  * @cfg {String} rid the reference id
30009  * @cfg {Boolean} active (true|false) Is item active default false
30010  * @cfg {Boolean} disabled (true|false) Is item active default false
30011  * @cfg {String} html
30012  * @cfg {String} position (top|bottom) text position default bottom
30013  * @cfg {String} icon show icon instead of number
30014  * 
30015  * @constructor
30016  * Create a new NavProgressItem
30017  * @param {Object} config The config object
30018  */
30019 Roo.bootstrap.NavProgressItem = function(config){
30020     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30021     this.addEvents({
30022         // raw events
30023         /**
30024          * @event click
30025          * The raw click event for the entire grid.
30026          * @param {Roo.bootstrap.NavProgressItem} this
30027          * @param {Roo.EventObject} e
30028          */
30029         "click" : true
30030     });
30031    
30032 };
30033
30034 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30035     
30036     rid : '',
30037     active : false,
30038     disabled : false,
30039     html : '',
30040     position : 'bottom',
30041     icon : false,
30042     
30043     getAutoCreate : function()
30044     {
30045         var iconCls = 'roo-navigation-bar-item-icon';
30046         
30047         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30048         
30049         var cfg = {
30050             tag: 'li',
30051             cls: 'roo-navigation-bar-item',
30052             cn : [
30053                 {
30054                     tag : 'i',
30055                     cls : iconCls
30056                 }
30057             ]
30058         };
30059         
30060         if(this.active){
30061             cfg.cls += ' active';
30062         }
30063         if(this.disabled){
30064             cfg.cls += ' disabled';
30065         }
30066         
30067         return cfg;
30068     },
30069     
30070     disable : function()
30071     {
30072         this.setDisabled(true);
30073     },
30074     
30075     enable : function()
30076     {
30077         this.setDisabled(false);
30078     },
30079     
30080     initEvents: function() 
30081     {
30082         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30083         
30084         this.iconEl.on('click', this.onClick, this);
30085     },
30086     
30087     onClick : function(e)
30088     {
30089         e.preventDefault();
30090         
30091         if(this.disabled){
30092             return;
30093         }
30094         
30095         if(this.fireEvent('click', this, e) === false){
30096             return;
30097         };
30098         
30099         this.parent().setActiveItem(this);
30100     },
30101     
30102     isActive: function () 
30103     {
30104         return this.active;
30105     },
30106     
30107     setActive : function(state)
30108     {
30109         if(this.active == state){
30110             return;
30111         }
30112         
30113         this.active = state;
30114         
30115         if (state) {
30116             this.el.addClass('active');
30117             return;
30118         }
30119         
30120         this.el.removeClass('active');
30121         
30122         return;
30123     },
30124     
30125     setDisabled : function(state)
30126     {
30127         if(this.disabled == state){
30128             return;
30129         }
30130         
30131         this.disabled = state;
30132         
30133         if (state) {
30134             this.el.addClass('disabled');
30135             return;
30136         }
30137         
30138         this.el.removeClass('disabled');
30139     },
30140     
30141     tooltipEl : function()
30142     {
30143         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30144     }
30145 });
30146  
30147
30148  /*
30149  * - LGPL
30150  *
30151  * FieldLabel
30152  * 
30153  */
30154
30155 /**
30156  * @class Roo.bootstrap.FieldLabel
30157  * @extends Roo.bootstrap.Component
30158  * Bootstrap FieldLabel class
30159  * @cfg {String} html contents of the element
30160  * @cfg {String} tag tag of the element default label
30161  * @cfg {String} cls class of the element
30162  * @cfg {String} target label target 
30163  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30164  * @cfg {String} invalidClass default "text-warning"
30165  * @cfg {String} validClass default "text-success"
30166  * @cfg {String} iconTooltip default "This field is required"
30167  * @cfg {String} indicatorpos (left|right) default left
30168  * 
30169  * @constructor
30170  * Create a new FieldLabel
30171  * @param {Object} config The config object
30172  */
30173
30174 Roo.bootstrap.FieldLabel = function(config){
30175     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30176     
30177     this.addEvents({
30178             /**
30179              * @event invalid
30180              * Fires after the field has been marked as invalid.
30181              * @param {Roo.form.FieldLabel} this
30182              * @param {String} msg The validation message
30183              */
30184             invalid : true,
30185             /**
30186              * @event valid
30187              * Fires after the field has been validated with no errors.
30188              * @param {Roo.form.FieldLabel} this
30189              */
30190             valid : true
30191         });
30192 };
30193
30194 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30195     
30196     tag: 'label',
30197     cls: '',
30198     html: '',
30199     target: '',
30200     allowBlank : true,
30201     invalidClass : 'has-warning',
30202     validClass : 'has-success',
30203     iconTooltip : 'This field is required',
30204     indicatorpos : 'left',
30205     
30206     getAutoCreate : function(){
30207         
30208         var cls = "";
30209         if (!this.allowBlank) {
30210             cls  = "visible";
30211         }
30212         
30213         var cfg = {
30214             tag : this.tag,
30215             cls : 'roo-bootstrap-field-label ' + this.cls,
30216             for : this.target,
30217             cn : [
30218                 {
30219                     tag : 'i',
30220                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30221                     tooltip : this.iconTooltip
30222                 },
30223                 {
30224                     tag : 'span',
30225                     html : this.html
30226                 }
30227             ] 
30228         };
30229         
30230         if(this.indicatorpos == 'right'){
30231             var cfg = {
30232                 tag : this.tag,
30233                 cls : 'roo-bootstrap-field-label ' + this.cls,
30234                 for : this.target,
30235                 cn : [
30236                     {
30237                         tag : 'span',
30238                         html : this.html
30239                     },
30240                     {
30241                         tag : 'i',
30242                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30243                         tooltip : this.iconTooltip
30244                     }
30245                 ] 
30246             };
30247         }
30248         
30249         return cfg;
30250     },
30251     
30252     initEvents: function() 
30253     {
30254         Roo.bootstrap.Element.superclass.initEvents.call(this);
30255         
30256         this.indicator = this.indicatorEl();
30257         
30258         if(this.indicator){
30259             this.indicator.removeClass('visible');
30260             this.indicator.addClass('invisible');
30261         }
30262         
30263         Roo.bootstrap.FieldLabel.register(this);
30264     },
30265     
30266     indicatorEl : function()
30267     {
30268         var indicator = this.el.select('i.roo-required-indicator',true).first();
30269         
30270         if(!indicator){
30271             return false;
30272         }
30273         
30274         return indicator;
30275         
30276     },
30277     
30278     /**
30279      * Mark this field as valid
30280      */
30281     markValid : function()
30282     {
30283         if(this.indicator){
30284             this.indicator.removeClass('visible');
30285             this.indicator.addClass('invisible');
30286         }
30287         
30288         this.el.removeClass(this.invalidClass);
30289         
30290         this.el.addClass(this.validClass);
30291         
30292         this.fireEvent('valid', this);
30293     },
30294     
30295     /**
30296      * Mark this field as invalid
30297      * @param {String} msg The validation message
30298      */
30299     markInvalid : function(msg)
30300     {
30301         if(this.indicator){
30302             this.indicator.removeClass('invisible');
30303             this.indicator.addClass('visible');
30304         }
30305         
30306         this.el.removeClass(this.validClass);
30307         
30308         this.el.addClass(this.invalidClass);
30309         
30310         this.fireEvent('invalid', this, msg);
30311     }
30312     
30313    
30314 });
30315
30316 Roo.apply(Roo.bootstrap.FieldLabel, {
30317     
30318     groups: {},
30319     
30320      /**
30321     * register a FieldLabel Group
30322     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30323     */
30324     register : function(label)
30325     {
30326         if(this.groups.hasOwnProperty(label.target)){
30327             return;
30328         }
30329      
30330         this.groups[label.target] = label;
30331         
30332     },
30333     /**
30334     * fetch a FieldLabel Group based on the target
30335     * @param {string} target
30336     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30337     */
30338     get: function(target) {
30339         if (typeof(this.groups[target]) == 'undefined') {
30340             return false;
30341         }
30342         
30343         return this.groups[target] ;
30344     }
30345 });
30346
30347  
30348
30349  /*
30350  * - LGPL
30351  *
30352  * page DateSplitField.
30353  * 
30354  */
30355
30356
30357 /**
30358  * @class Roo.bootstrap.DateSplitField
30359  * @extends Roo.bootstrap.Component
30360  * Bootstrap DateSplitField class
30361  * @cfg {string} fieldLabel - the label associated
30362  * @cfg {Number} labelWidth set the width of label (0-12)
30363  * @cfg {String} labelAlign (top|left)
30364  * @cfg {Boolean} dayAllowBlank (true|false) default false
30365  * @cfg {Boolean} monthAllowBlank (true|false) default false
30366  * @cfg {Boolean} yearAllowBlank (true|false) default false
30367  * @cfg {string} dayPlaceholder 
30368  * @cfg {string} monthPlaceholder
30369  * @cfg {string} yearPlaceholder
30370  * @cfg {string} dayFormat default 'd'
30371  * @cfg {string} monthFormat default 'm'
30372  * @cfg {string} yearFormat default 'Y'
30373  * @cfg {Number} labellg set the width of label (1-12)
30374  * @cfg {Number} labelmd set the width of label (1-12)
30375  * @cfg {Number} labelsm set the width of label (1-12)
30376  * @cfg {Number} labelxs set the width of label (1-12)
30377
30378  *     
30379  * @constructor
30380  * Create a new DateSplitField
30381  * @param {Object} config The config object
30382  */
30383
30384 Roo.bootstrap.DateSplitField = function(config){
30385     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30386     
30387     this.addEvents({
30388         // raw events
30389          /**
30390          * @event years
30391          * getting the data of years
30392          * @param {Roo.bootstrap.DateSplitField} this
30393          * @param {Object} years
30394          */
30395         "years" : true,
30396         /**
30397          * @event days
30398          * getting the data of days
30399          * @param {Roo.bootstrap.DateSplitField} this
30400          * @param {Object} days
30401          */
30402         "days" : true,
30403         /**
30404          * @event invalid
30405          * Fires after the field has been marked as invalid.
30406          * @param {Roo.form.Field} this
30407          * @param {String} msg The validation message
30408          */
30409         invalid : true,
30410        /**
30411          * @event valid
30412          * Fires after the field has been validated with no errors.
30413          * @param {Roo.form.Field} this
30414          */
30415         valid : true
30416     });
30417 };
30418
30419 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30420     
30421     fieldLabel : '',
30422     labelAlign : 'top',
30423     labelWidth : 3,
30424     dayAllowBlank : false,
30425     monthAllowBlank : false,
30426     yearAllowBlank : false,
30427     dayPlaceholder : '',
30428     monthPlaceholder : '',
30429     yearPlaceholder : '',
30430     dayFormat : 'd',
30431     monthFormat : 'm',
30432     yearFormat : 'Y',
30433     isFormField : true,
30434     labellg : 0,
30435     labelmd : 0,
30436     labelsm : 0,
30437     labelxs : 0,
30438     
30439     getAutoCreate : function()
30440     {
30441         var cfg = {
30442             tag : 'div',
30443             cls : 'row roo-date-split-field-group',
30444             cn : [
30445                 {
30446                     tag : 'input',
30447                     type : 'hidden',
30448                     cls : 'form-hidden-field roo-date-split-field-group-value',
30449                     name : this.name
30450                 }
30451             ]
30452         };
30453         
30454         var labelCls = 'col-md-12';
30455         var contentCls = 'col-md-4';
30456         
30457         if(this.fieldLabel){
30458             
30459             var label = {
30460                 tag : 'div',
30461                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30462                 cn : [
30463                     {
30464                         tag : 'label',
30465                         html : this.fieldLabel
30466                     }
30467                 ]
30468             };
30469             
30470             if(this.labelAlign == 'left'){
30471             
30472                 if(this.labelWidth > 12){
30473                     label.style = "width: " + this.labelWidth + 'px';
30474                 }
30475
30476                 if(this.labelWidth < 13 && this.labelmd == 0){
30477                     this.labelmd = this.labelWidth;
30478                 }
30479
30480                 if(this.labellg > 0){
30481                     labelCls = ' col-lg-' + this.labellg;
30482                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30483                 }
30484
30485                 if(this.labelmd > 0){
30486                     labelCls = ' col-md-' + this.labelmd;
30487                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30488                 }
30489
30490                 if(this.labelsm > 0){
30491                     labelCls = ' col-sm-' + this.labelsm;
30492                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30493                 }
30494
30495                 if(this.labelxs > 0){
30496                     labelCls = ' col-xs-' + this.labelxs;
30497                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30498                 }
30499             }
30500             
30501             label.cls += ' ' + labelCls;
30502             
30503             cfg.cn.push(label);
30504         }
30505         
30506         Roo.each(['day', 'month', 'year'], function(t){
30507             cfg.cn.push({
30508                 tag : 'div',
30509                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30510             });
30511         }, this);
30512         
30513         return cfg;
30514     },
30515     
30516     inputEl: function ()
30517     {
30518         return this.el.select('.roo-date-split-field-group-value', true).first();
30519     },
30520     
30521     onRender : function(ct, position) 
30522     {
30523         var _this = this;
30524         
30525         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30526         
30527         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30528         
30529         this.dayField = new Roo.bootstrap.ComboBox({
30530             allowBlank : this.dayAllowBlank,
30531             alwaysQuery : true,
30532             displayField : 'value',
30533             editable : false,
30534             fieldLabel : '',
30535             forceSelection : true,
30536             mode : 'local',
30537             placeholder : this.dayPlaceholder,
30538             selectOnFocus : true,
30539             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30540             triggerAction : 'all',
30541             typeAhead : true,
30542             valueField : 'value',
30543             store : new Roo.data.SimpleStore({
30544                 data : (function() {    
30545                     var days = [];
30546                     _this.fireEvent('days', _this, days);
30547                     return days;
30548                 })(),
30549                 fields : [ 'value' ]
30550             }),
30551             listeners : {
30552                 select : function (_self, record, index)
30553                 {
30554                     _this.setValue(_this.getValue());
30555                 }
30556             }
30557         });
30558
30559         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30560         
30561         this.monthField = new Roo.bootstrap.MonthField({
30562             after : '<i class=\"fa fa-calendar\"></i>',
30563             allowBlank : this.monthAllowBlank,
30564             placeholder : this.monthPlaceholder,
30565             readOnly : true,
30566             listeners : {
30567                 render : function (_self)
30568                 {
30569                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30570                         e.preventDefault();
30571                         _self.focus();
30572                     });
30573                 },
30574                 select : function (_self, oldvalue, newvalue)
30575                 {
30576                     _this.setValue(_this.getValue());
30577                 }
30578             }
30579         });
30580         
30581         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30582         
30583         this.yearField = new Roo.bootstrap.ComboBox({
30584             allowBlank : this.yearAllowBlank,
30585             alwaysQuery : true,
30586             displayField : 'value',
30587             editable : false,
30588             fieldLabel : '',
30589             forceSelection : true,
30590             mode : 'local',
30591             placeholder : this.yearPlaceholder,
30592             selectOnFocus : true,
30593             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30594             triggerAction : 'all',
30595             typeAhead : true,
30596             valueField : 'value',
30597             store : new Roo.data.SimpleStore({
30598                 data : (function() {
30599                     var years = [];
30600                     _this.fireEvent('years', _this, years);
30601                     return years;
30602                 })(),
30603                 fields : [ 'value' ]
30604             }),
30605             listeners : {
30606                 select : function (_self, record, index)
30607                 {
30608                     _this.setValue(_this.getValue());
30609                 }
30610             }
30611         });
30612
30613         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30614     },
30615     
30616     setValue : function(v, format)
30617     {
30618         this.inputEl.dom.value = v;
30619         
30620         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30621         
30622         var d = Date.parseDate(v, f);
30623         
30624         if(!d){
30625             this.validate();
30626             return;
30627         }
30628         
30629         this.setDay(d.format(this.dayFormat));
30630         this.setMonth(d.format(this.monthFormat));
30631         this.setYear(d.format(this.yearFormat));
30632         
30633         this.validate();
30634         
30635         return;
30636     },
30637     
30638     setDay : function(v)
30639     {
30640         this.dayField.setValue(v);
30641         this.inputEl.dom.value = this.getValue();
30642         this.validate();
30643         return;
30644     },
30645     
30646     setMonth : function(v)
30647     {
30648         this.monthField.setValue(v, true);
30649         this.inputEl.dom.value = this.getValue();
30650         this.validate();
30651         return;
30652     },
30653     
30654     setYear : function(v)
30655     {
30656         this.yearField.setValue(v);
30657         this.inputEl.dom.value = this.getValue();
30658         this.validate();
30659         return;
30660     },
30661     
30662     getDay : function()
30663     {
30664         return this.dayField.getValue();
30665     },
30666     
30667     getMonth : function()
30668     {
30669         return this.monthField.getValue();
30670     },
30671     
30672     getYear : function()
30673     {
30674         return this.yearField.getValue();
30675     },
30676     
30677     getValue : function()
30678     {
30679         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30680         
30681         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30682         
30683         return date;
30684     },
30685     
30686     reset : function()
30687     {
30688         this.setDay('');
30689         this.setMonth('');
30690         this.setYear('');
30691         this.inputEl.dom.value = '';
30692         this.validate();
30693         return;
30694     },
30695     
30696     validate : function()
30697     {
30698         var d = this.dayField.validate();
30699         var m = this.monthField.validate();
30700         var y = this.yearField.validate();
30701         
30702         var valid = true;
30703         
30704         if(
30705                 (!this.dayAllowBlank && !d) ||
30706                 (!this.monthAllowBlank && !m) ||
30707                 (!this.yearAllowBlank && !y)
30708         ){
30709             valid = false;
30710         }
30711         
30712         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30713             return valid;
30714         }
30715         
30716         if(valid){
30717             this.markValid();
30718             return valid;
30719         }
30720         
30721         this.markInvalid();
30722         
30723         return valid;
30724     },
30725     
30726     markValid : function()
30727     {
30728         
30729         var label = this.el.select('label', true).first();
30730         var icon = this.el.select('i.fa-star', true).first();
30731
30732         if(label && icon){
30733             icon.remove();
30734         }
30735         
30736         this.fireEvent('valid', this);
30737     },
30738     
30739      /**
30740      * Mark this field as invalid
30741      * @param {String} msg The validation message
30742      */
30743     markInvalid : function(msg)
30744     {
30745         
30746         var label = this.el.select('label', true).first();
30747         var icon = this.el.select('i.fa-star', true).first();
30748
30749         if(label && !icon){
30750             this.el.select('.roo-date-split-field-label', true).createChild({
30751                 tag : 'i',
30752                 cls : 'text-danger fa fa-lg fa-star',
30753                 tooltip : 'This field is required',
30754                 style : 'margin-right:5px;'
30755             }, label, true);
30756         }
30757         
30758         this.fireEvent('invalid', this, msg);
30759     },
30760     
30761     clearInvalid : function()
30762     {
30763         var label = this.el.select('label', true).first();
30764         var icon = this.el.select('i.fa-star', true).first();
30765
30766         if(label && icon){
30767             icon.remove();
30768         }
30769         
30770         this.fireEvent('valid', this);
30771     },
30772     
30773     getName: function()
30774     {
30775         return this.name;
30776     }
30777     
30778 });
30779
30780  /**
30781  *
30782  * This is based on 
30783  * http://masonry.desandro.com
30784  *
30785  * The idea is to render all the bricks based on vertical width...
30786  *
30787  * The original code extends 'outlayer' - we might need to use that....
30788  * 
30789  */
30790
30791
30792 /**
30793  * @class Roo.bootstrap.LayoutMasonry
30794  * @extends Roo.bootstrap.Component
30795  * Bootstrap Layout Masonry class
30796  * 
30797  * @constructor
30798  * Create a new Element
30799  * @param {Object} config The config object
30800  */
30801
30802 Roo.bootstrap.LayoutMasonry = function(config){
30803     
30804     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30805     
30806     this.bricks = [];
30807     
30808     Roo.bootstrap.LayoutMasonry.register(this);
30809     
30810     this.addEvents({
30811         // raw events
30812         /**
30813          * @event layout
30814          * Fire after layout the items
30815          * @param {Roo.bootstrap.LayoutMasonry} this
30816          * @param {Roo.EventObject} e
30817          */
30818         "layout" : true
30819     });
30820     
30821 };
30822
30823 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30824     
30825     /**
30826      * @cfg {Boolean} isLayoutInstant = no animation?
30827      */   
30828     isLayoutInstant : false, // needed?
30829    
30830     /**
30831      * @cfg {Number} boxWidth  width of the columns
30832      */   
30833     boxWidth : 450,
30834     
30835       /**
30836      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30837      */   
30838     boxHeight : 0,
30839     
30840     /**
30841      * @cfg {Number} padWidth padding below box..
30842      */   
30843     padWidth : 10, 
30844     
30845     /**
30846      * @cfg {Number} gutter gutter width..
30847      */   
30848     gutter : 10,
30849     
30850      /**
30851      * @cfg {Number} maxCols maximum number of columns
30852      */   
30853     
30854     maxCols: 0,
30855     
30856     /**
30857      * @cfg {Boolean} isAutoInitial defalut true
30858      */   
30859     isAutoInitial : true, 
30860     
30861     containerWidth: 0,
30862     
30863     /**
30864      * @cfg {Boolean} isHorizontal defalut false
30865      */   
30866     isHorizontal : false, 
30867
30868     currentSize : null,
30869     
30870     tag: 'div',
30871     
30872     cls: '',
30873     
30874     bricks: null, //CompositeElement
30875     
30876     cols : 1,
30877     
30878     _isLayoutInited : false,
30879     
30880 //    isAlternative : false, // only use for vertical layout...
30881     
30882     /**
30883      * @cfg {Number} alternativePadWidth padding below box..
30884      */   
30885     alternativePadWidth : 50,
30886     
30887     selectedBrick : [],
30888     
30889     getAutoCreate : function(){
30890         
30891         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30892         
30893         var cfg = {
30894             tag: this.tag,
30895             cls: 'blog-masonary-wrapper ' + this.cls,
30896             cn : {
30897                 cls : 'mas-boxes masonary'
30898             }
30899         };
30900         
30901         return cfg;
30902     },
30903     
30904     getChildContainer: function( )
30905     {
30906         if (this.boxesEl) {
30907             return this.boxesEl;
30908         }
30909         
30910         this.boxesEl = this.el.select('.mas-boxes').first();
30911         
30912         return this.boxesEl;
30913     },
30914     
30915     
30916     initEvents : function()
30917     {
30918         var _this = this;
30919         
30920         if(this.isAutoInitial){
30921             Roo.log('hook children rendered');
30922             this.on('childrenrendered', function() {
30923                 Roo.log('children rendered');
30924                 _this.initial();
30925             } ,this);
30926         }
30927     },
30928     
30929     initial : function()
30930     {
30931         this.selectedBrick = [];
30932         
30933         this.currentSize = this.el.getBox(true);
30934         
30935         Roo.EventManager.onWindowResize(this.resize, this); 
30936
30937         if(!this.isAutoInitial){
30938             this.layout();
30939             return;
30940         }
30941         
30942         this.layout();
30943         
30944         return;
30945         //this.layout.defer(500,this);
30946         
30947     },
30948     
30949     resize : function()
30950     {
30951         var cs = this.el.getBox(true);
30952         
30953         if (
30954                 this.currentSize.width == cs.width && 
30955                 this.currentSize.x == cs.x && 
30956                 this.currentSize.height == cs.height && 
30957                 this.currentSize.y == cs.y 
30958         ) {
30959             Roo.log("no change in with or X or Y");
30960             return;
30961         }
30962         
30963         this.currentSize = cs;
30964         
30965         this.layout();
30966         
30967     },
30968     
30969     layout : function()
30970     {   
30971         this._resetLayout();
30972         
30973         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30974         
30975         this.layoutItems( isInstant );
30976       
30977         this._isLayoutInited = true;
30978         
30979         this.fireEvent('layout', this);
30980         
30981     },
30982     
30983     _resetLayout : function()
30984     {
30985         if(this.isHorizontal){
30986             this.horizontalMeasureColumns();
30987             return;
30988         }
30989         
30990         this.verticalMeasureColumns();
30991         
30992     },
30993     
30994     verticalMeasureColumns : function()
30995     {
30996         this.getContainerWidth();
30997         
30998 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30999 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31000 //            return;
31001 //        }
31002         
31003         var boxWidth = this.boxWidth + this.padWidth;
31004         
31005         if(this.containerWidth < this.boxWidth){
31006             boxWidth = this.containerWidth
31007         }
31008         
31009         var containerWidth = this.containerWidth;
31010         
31011         var cols = Math.floor(containerWidth / boxWidth);
31012         
31013         this.cols = Math.max( cols, 1 );
31014         
31015         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31016         
31017         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31018         
31019         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31020         
31021         this.colWidth = boxWidth + avail - this.padWidth;
31022         
31023         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31024         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31025     },
31026     
31027     horizontalMeasureColumns : function()
31028     {
31029         this.getContainerWidth();
31030         
31031         var boxWidth = this.boxWidth;
31032         
31033         if(this.containerWidth < boxWidth){
31034             boxWidth = this.containerWidth;
31035         }
31036         
31037         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31038         
31039         this.el.setHeight(boxWidth);
31040         
31041     },
31042     
31043     getContainerWidth : function()
31044     {
31045         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31046     },
31047     
31048     layoutItems : function( isInstant )
31049     {
31050         Roo.log(this.bricks);
31051         
31052         var items = Roo.apply([], this.bricks);
31053         
31054         if(this.isHorizontal){
31055             this._horizontalLayoutItems( items , isInstant );
31056             return;
31057         }
31058         
31059 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31060 //            this._verticalAlternativeLayoutItems( items , isInstant );
31061 //            return;
31062 //        }
31063         
31064         this._verticalLayoutItems( items , isInstant );
31065         
31066     },
31067     
31068     _verticalLayoutItems : function ( items , isInstant)
31069     {
31070         if ( !items || !items.length ) {
31071             return;
31072         }
31073         
31074         var standard = [
31075             ['xs', 'xs', 'xs', 'tall'],
31076             ['xs', 'xs', 'tall'],
31077             ['xs', 'xs', 'sm'],
31078             ['xs', 'xs', 'xs'],
31079             ['xs', 'tall'],
31080             ['xs', 'sm'],
31081             ['xs', 'xs'],
31082             ['xs'],
31083             
31084             ['sm', 'xs', 'xs'],
31085             ['sm', 'xs'],
31086             ['sm'],
31087             
31088             ['tall', 'xs', 'xs', 'xs'],
31089             ['tall', 'xs', 'xs'],
31090             ['tall', 'xs'],
31091             ['tall']
31092             
31093         ];
31094         
31095         var queue = [];
31096         
31097         var boxes = [];
31098         
31099         var box = [];
31100         
31101         Roo.each(items, function(item, k){
31102             
31103             switch (item.size) {
31104                 // these layouts take up a full box,
31105                 case 'md' :
31106                 case 'md-left' :
31107                 case 'md-right' :
31108                 case 'wide' :
31109                     
31110                     if(box.length){
31111                         boxes.push(box);
31112                         box = [];
31113                     }
31114                     
31115                     boxes.push([item]);
31116                     
31117                     break;
31118                     
31119                 case 'xs' :
31120                 case 'sm' :
31121                 case 'tall' :
31122                     
31123                     box.push(item);
31124                     
31125                     break;
31126                 default :
31127                     break;
31128                     
31129             }
31130             
31131         }, this);
31132         
31133         if(box.length){
31134             boxes.push(box);
31135             box = [];
31136         }
31137         
31138         var filterPattern = function(box, length)
31139         {
31140             if(!box.length){
31141                 return;
31142             }
31143             
31144             var match = false;
31145             
31146             var pattern = box.slice(0, length);
31147             
31148             var format = [];
31149             
31150             Roo.each(pattern, function(i){
31151                 format.push(i.size);
31152             }, this);
31153             
31154             Roo.each(standard, function(s){
31155                 
31156                 if(String(s) != String(format)){
31157                     return;
31158                 }
31159                 
31160                 match = true;
31161                 return false;
31162                 
31163             }, this);
31164             
31165             if(!match && length == 1){
31166                 return;
31167             }
31168             
31169             if(!match){
31170                 filterPattern(box, length - 1);
31171                 return;
31172             }
31173                 
31174             queue.push(pattern);
31175
31176             box = box.slice(length, box.length);
31177
31178             filterPattern(box, 4);
31179
31180             return;
31181             
31182         }
31183         
31184         Roo.each(boxes, function(box, k){
31185             
31186             if(!box.length){
31187                 return;
31188             }
31189             
31190             if(box.length == 1){
31191                 queue.push(box);
31192                 return;
31193             }
31194             
31195             filterPattern(box, 4);
31196             
31197         }, this);
31198         
31199         this._processVerticalLayoutQueue( queue, isInstant );
31200         
31201     },
31202     
31203 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31204 //    {
31205 //        if ( !items || !items.length ) {
31206 //            return;
31207 //        }
31208 //
31209 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31210 //        
31211 //    },
31212     
31213     _horizontalLayoutItems : function ( items , isInstant)
31214     {
31215         if ( !items || !items.length || items.length < 3) {
31216             return;
31217         }
31218         
31219         items.reverse();
31220         
31221         var eItems = items.slice(0, 3);
31222         
31223         items = items.slice(3, items.length);
31224         
31225         var standard = [
31226             ['xs', 'xs', 'xs', 'wide'],
31227             ['xs', 'xs', 'wide'],
31228             ['xs', 'xs', 'sm'],
31229             ['xs', 'xs', 'xs'],
31230             ['xs', 'wide'],
31231             ['xs', 'sm'],
31232             ['xs', 'xs'],
31233             ['xs'],
31234             
31235             ['sm', 'xs', 'xs'],
31236             ['sm', 'xs'],
31237             ['sm'],
31238             
31239             ['wide', 'xs', 'xs', 'xs'],
31240             ['wide', 'xs', 'xs'],
31241             ['wide', 'xs'],
31242             ['wide'],
31243             
31244             ['wide-thin']
31245         ];
31246         
31247         var queue = [];
31248         
31249         var boxes = [];
31250         
31251         var box = [];
31252         
31253         Roo.each(items, function(item, k){
31254             
31255             switch (item.size) {
31256                 case 'md' :
31257                 case 'md-left' :
31258                 case 'md-right' :
31259                 case 'tall' :
31260                     
31261                     if(box.length){
31262                         boxes.push(box);
31263                         box = [];
31264                     }
31265                     
31266                     boxes.push([item]);
31267                     
31268                     break;
31269                     
31270                 case 'xs' :
31271                 case 'sm' :
31272                 case 'wide' :
31273                 case 'wide-thin' :
31274                     
31275                     box.push(item);
31276                     
31277                     break;
31278                 default :
31279                     break;
31280                     
31281             }
31282             
31283         }, this);
31284         
31285         if(box.length){
31286             boxes.push(box);
31287             box = [];
31288         }
31289         
31290         var filterPattern = function(box, length)
31291         {
31292             if(!box.length){
31293                 return;
31294             }
31295             
31296             var match = false;
31297             
31298             var pattern = box.slice(0, length);
31299             
31300             var format = [];
31301             
31302             Roo.each(pattern, function(i){
31303                 format.push(i.size);
31304             }, this);
31305             
31306             Roo.each(standard, function(s){
31307                 
31308                 if(String(s) != String(format)){
31309                     return;
31310                 }
31311                 
31312                 match = true;
31313                 return false;
31314                 
31315             }, this);
31316             
31317             if(!match && length == 1){
31318                 return;
31319             }
31320             
31321             if(!match){
31322                 filterPattern(box, length - 1);
31323                 return;
31324             }
31325                 
31326             queue.push(pattern);
31327
31328             box = box.slice(length, box.length);
31329
31330             filterPattern(box, 4);
31331
31332             return;
31333             
31334         }
31335         
31336         Roo.each(boxes, function(box, k){
31337             
31338             if(!box.length){
31339                 return;
31340             }
31341             
31342             if(box.length == 1){
31343                 queue.push(box);
31344                 return;
31345             }
31346             
31347             filterPattern(box, 4);
31348             
31349         }, this);
31350         
31351         
31352         var prune = [];
31353         
31354         var pos = this.el.getBox(true);
31355         
31356         var minX = pos.x;
31357         
31358         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31359         
31360         var hit_end = false;
31361         
31362         Roo.each(queue, function(box){
31363             
31364             if(hit_end){
31365                 
31366                 Roo.each(box, function(b){
31367                 
31368                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31369                     b.el.hide();
31370
31371                 }, this);
31372
31373                 return;
31374             }
31375             
31376             var mx = 0;
31377             
31378             Roo.each(box, function(b){
31379                 
31380                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31381                 b.el.show();
31382
31383                 mx = Math.max(mx, b.x);
31384                 
31385             }, this);
31386             
31387             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31388             
31389             if(maxX < minX){
31390                 
31391                 Roo.each(box, function(b){
31392                 
31393                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31394                     b.el.hide();
31395                     
31396                 }, this);
31397                 
31398                 hit_end = true;
31399                 
31400                 return;
31401             }
31402             
31403             prune.push(box);
31404             
31405         }, this);
31406         
31407         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31408     },
31409     
31410     /** Sets position of item in DOM
31411     * @param {Element} item
31412     * @param {Number} x - horizontal position
31413     * @param {Number} y - vertical position
31414     * @param {Boolean} isInstant - disables transitions
31415     */
31416     _processVerticalLayoutQueue : function( queue, isInstant )
31417     {
31418         var pos = this.el.getBox(true);
31419         var x = pos.x;
31420         var y = pos.y;
31421         var maxY = [];
31422         
31423         for (var i = 0; i < this.cols; i++){
31424             maxY[i] = pos.y;
31425         }
31426         
31427         Roo.each(queue, function(box, k){
31428             
31429             var col = k % this.cols;
31430             
31431             Roo.each(box, function(b,kk){
31432                 
31433                 b.el.position('absolute');
31434                 
31435                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31436                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31437                 
31438                 if(b.size == 'md-left' || b.size == 'md-right'){
31439                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31440                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31441                 }
31442                 
31443                 b.el.setWidth(width);
31444                 b.el.setHeight(height);
31445                 // iframe?
31446                 b.el.select('iframe',true).setSize(width,height);
31447                 
31448             }, this);
31449             
31450             for (var i = 0; i < this.cols; i++){
31451                 
31452                 if(maxY[i] < maxY[col]){
31453                     col = i;
31454                     continue;
31455                 }
31456                 
31457                 col = Math.min(col, i);
31458                 
31459             }
31460             
31461             x = pos.x + col * (this.colWidth + this.padWidth);
31462             
31463             y = maxY[col];
31464             
31465             var positions = [];
31466             
31467             switch (box.length){
31468                 case 1 :
31469                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31470                     break;
31471                 case 2 :
31472                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31473                     break;
31474                 case 3 :
31475                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31476                     break;
31477                 case 4 :
31478                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31479                     break;
31480                 default :
31481                     break;
31482             }
31483             
31484             Roo.each(box, function(b,kk){
31485                 
31486                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31487                 
31488                 var sz = b.el.getSize();
31489                 
31490                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31491                 
31492             }, this);
31493             
31494         }, this);
31495         
31496         var mY = 0;
31497         
31498         for (var i = 0; i < this.cols; i++){
31499             mY = Math.max(mY, maxY[i]);
31500         }
31501         
31502         this.el.setHeight(mY - pos.y);
31503         
31504     },
31505     
31506 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31507 //    {
31508 //        var pos = this.el.getBox(true);
31509 //        var x = pos.x;
31510 //        var y = pos.y;
31511 //        var maxX = pos.right;
31512 //        
31513 //        var maxHeight = 0;
31514 //        
31515 //        Roo.each(items, function(item, k){
31516 //            
31517 //            var c = k % 2;
31518 //            
31519 //            item.el.position('absolute');
31520 //                
31521 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31522 //
31523 //            item.el.setWidth(width);
31524 //
31525 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31526 //
31527 //            item.el.setHeight(height);
31528 //            
31529 //            if(c == 0){
31530 //                item.el.setXY([x, y], isInstant ? false : true);
31531 //            } else {
31532 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31533 //            }
31534 //            
31535 //            y = y + height + this.alternativePadWidth;
31536 //            
31537 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31538 //            
31539 //        }, this);
31540 //        
31541 //        this.el.setHeight(maxHeight);
31542 //        
31543 //    },
31544     
31545     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31546     {
31547         var pos = this.el.getBox(true);
31548         
31549         var minX = pos.x;
31550         var minY = pos.y;
31551         
31552         var maxX = pos.right;
31553         
31554         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31555         
31556         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31557         
31558         Roo.each(queue, function(box, k){
31559             
31560             Roo.each(box, function(b, kk){
31561                 
31562                 b.el.position('absolute');
31563                 
31564                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31565                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31566                 
31567                 if(b.size == 'md-left' || b.size == 'md-right'){
31568                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31569                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31570                 }
31571                 
31572                 b.el.setWidth(width);
31573                 b.el.setHeight(height);
31574                 
31575             }, this);
31576             
31577             if(!box.length){
31578                 return;
31579             }
31580             
31581             var positions = [];
31582             
31583             switch (box.length){
31584                 case 1 :
31585                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31586                     break;
31587                 case 2 :
31588                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31589                     break;
31590                 case 3 :
31591                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31592                     break;
31593                 case 4 :
31594                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31595                     break;
31596                 default :
31597                     break;
31598             }
31599             
31600             Roo.each(box, function(b,kk){
31601                 
31602                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31603                 
31604                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31605                 
31606             }, this);
31607             
31608         }, this);
31609         
31610     },
31611     
31612     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31613     {
31614         Roo.each(eItems, function(b,k){
31615             
31616             b.size = (k == 0) ? 'sm' : 'xs';
31617             b.x = (k == 0) ? 2 : 1;
31618             b.y = (k == 0) ? 2 : 1;
31619             
31620             b.el.position('absolute');
31621             
31622             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31623                 
31624             b.el.setWidth(width);
31625             
31626             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31627             
31628             b.el.setHeight(height);
31629             
31630         }, this);
31631
31632         var positions = [];
31633         
31634         positions.push({
31635             x : maxX - this.unitWidth * 2 - this.gutter,
31636             y : minY
31637         });
31638         
31639         positions.push({
31640             x : maxX - this.unitWidth,
31641             y : minY + (this.unitWidth + this.gutter) * 2
31642         });
31643         
31644         positions.push({
31645             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31646             y : minY
31647         });
31648         
31649         Roo.each(eItems, function(b,k){
31650             
31651             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31652
31653         }, this);
31654         
31655     },
31656     
31657     getVerticalOneBoxColPositions : function(x, y, box)
31658     {
31659         var pos = [];
31660         
31661         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31662         
31663         if(box[0].size == 'md-left'){
31664             rand = 0;
31665         }
31666         
31667         if(box[0].size == 'md-right'){
31668             rand = 1;
31669         }
31670         
31671         pos.push({
31672             x : x + (this.unitWidth + this.gutter) * rand,
31673             y : y
31674         });
31675         
31676         return pos;
31677     },
31678     
31679     getVerticalTwoBoxColPositions : function(x, y, box)
31680     {
31681         var pos = [];
31682         
31683         if(box[0].size == 'xs'){
31684             
31685             pos.push({
31686                 x : x,
31687                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31688             });
31689
31690             pos.push({
31691                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31692                 y : y
31693             });
31694             
31695             return pos;
31696             
31697         }
31698         
31699         pos.push({
31700             x : x,
31701             y : y
31702         });
31703
31704         pos.push({
31705             x : x + (this.unitWidth + this.gutter) * 2,
31706             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31707         });
31708         
31709         return pos;
31710         
31711     },
31712     
31713     getVerticalThreeBoxColPositions : function(x, y, box)
31714     {
31715         var pos = [];
31716         
31717         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31718             
31719             pos.push({
31720                 x : x,
31721                 y : y
31722             });
31723
31724             pos.push({
31725                 x : x + (this.unitWidth + this.gutter) * 1,
31726                 y : y
31727             });
31728             
31729             pos.push({
31730                 x : x + (this.unitWidth + this.gutter) * 2,
31731                 y : y
31732             });
31733             
31734             return pos;
31735             
31736         }
31737         
31738         if(box[0].size == 'xs' && box[1].size == 'xs'){
31739             
31740             pos.push({
31741                 x : x,
31742                 y : y
31743             });
31744
31745             pos.push({
31746                 x : x,
31747                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31748             });
31749             
31750             pos.push({
31751                 x : x + (this.unitWidth + this.gutter) * 1,
31752                 y : y
31753             });
31754             
31755             return pos;
31756             
31757         }
31758         
31759         pos.push({
31760             x : x,
31761             y : y
31762         });
31763
31764         pos.push({
31765             x : x + (this.unitWidth + this.gutter) * 2,
31766             y : y
31767         });
31768
31769         pos.push({
31770             x : x + (this.unitWidth + this.gutter) * 2,
31771             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31772         });
31773             
31774         return pos;
31775         
31776     },
31777     
31778     getVerticalFourBoxColPositions : function(x, y, box)
31779     {
31780         var pos = [];
31781         
31782         if(box[0].size == 'xs'){
31783             
31784             pos.push({
31785                 x : x,
31786                 y : y
31787             });
31788
31789             pos.push({
31790                 x : x,
31791                 y : y + (this.unitHeight + this.gutter) * 1
31792             });
31793             
31794             pos.push({
31795                 x : x,
31796                 y : y + (this.unitHeight + this.gutter) * 2
31797             });
31798             
31799             pos.push({
31800                 x : x + (this.unitWidth + this.gutter) * 1,
31801                 y : y
31802             });
31803             
31804             return pos;
31805             
31806         }
31807         
31808         pos.push({
31809             x : x,
31810             y : y
31811         });
31812
31813         pos.push({
31814             x : x + (this.unitWidth + this.gutter) * 2,
31815             y : y
31816         });
31817
31818         pos.push({
31819             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31820             y : y + (this.unitHeight + this.gutter) * 1
31821         });
31822
31823         pos.push({
31824             x : x + (this.unitWidth + this.gutter) * 2,
31825             y : y + (this.unitWidth + this.gutter) * 2
31826         });
31827
31828         return pos;
31829         
31830     },
31831     
31832     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31833     {
31834         var pos = [];
31835         
31836         if(box[0].size == 'md-left'){
31837             pos.push({
31838                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31839                 y : minY
31840             });
31841             
31842             return pos;
31843         }
31844         
31845         if(box[0].size == 'md-right'){
31846             pos.push({
31847                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31848                 y : minY + (this.unitWidth + this.gutter) * 1
31849             });
31850             
31851             return pos;
31852         }
31853         
31854         var rand = Math.floor(Math.random() * (4 - box[0].y));
31855         
31856         pos.push({
31857             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31858             y : minY + (this.unitWidth + this.gutter) * rand
31859         });
31860         
31861         return pos;
31862         
31863     },
31864     
31865     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31866     {
31867         var pos = [];
31868         
31869         if(box[0].size == 'xs'){
31870             
31871             pos.push({
31872                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31873                 y : minY
31874             });
31875
31876             pos.push({
31877                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31878                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31879             });
31880             
31881             return pos;
31882             
31883         }
31884         
31885         pos.push({
31886             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31887             y : minY
31888         });
31889
31890         pos.push({
31891             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31892             y : minY + (this.unitWidth + this.gutter) * 2
31893         });
31894         
31895         return pos;
31896         
31897     },
31898     
31899     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31900     {
31901         var pos = [];
31902         
31903         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31904             
31905             pos.push({
31906                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31907                 y : minY
31908             });
31909
31910             pos.push({
31911                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31912                 y : minY + (this.unitWidth + this.gutter) * 1
31913             });
31914             
31915             pos.push({
31916                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31917                 y : minY + (this.unitWidth + this.gutter) * 2
31918             });
31919             
31920             return pos;
31921             
31922         }
31923         
31924         if(box[0].size == 'xs' && box[1].size == 'xs'){
31925             
31926             pos.push({
31927                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31928                 y : minY
31929             });
31930
31931             pos.push({
31932                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31933                 y : minY
31934             });
31935             
31936             pos.push({
31937                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31938                 y : minY + (this.unitWidth + this.gutter) * 1
31939             });
31940             
31941             return pos;
31942             
31943         }
31944         
31945         pos.push({
31946             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31947             y : minY
31948         });
31949
31950         pos.push({
31951             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31952             y : minY + (this.unitWidth + this.gutter) * 2
31953         });
31954
31955         pos.push({
31956             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31957             y : minY + (this.unitWidth + this.gutter) * 2
31958         });
31959             
31960         return pos;
31961         
31962     },
31963     
31964     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31965     {
31966         var pos = [];
31967         
31968         if(box[0].size == 'xs'){
31969             
31970             pos.push({
31971                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31972                 y : minY
31973             });
31974
31975             pos.push({
31976                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31977                 y : minY
31978             });
31979             
31980             pos.push({
31981                 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),
31982                 y : minY
31983             });
31984             
31985             pos.push({
31986                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31987                 y : minY + (this.unitWidth + this.gutter) * 1
31988             });
31989             
31990             return pos;
31991             
31992         }
31993         
31994         pos.push({
31995             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31996             y : minY
31997         });
31998         
31999         pos.push({
32000             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32001             y : minY + (this.unitWidth + this.gutter) * 2
32002         });
32003         
32004         pos.push({
32005             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32006             y : minY + (this.unitWidth + this.gutter) * 2
32007         });
32008         
32009         pos.push({
32010             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),
32011             y : minY + (this.unitWidth + this.gutter) * 2
32012         });
32013
32014         return pos;
32015         
32016     },
32017     
32018     /**
32019     * remove a Masonry Brick
32020     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32021     */
32022     removeBrick : function(brick_id)
32023     {
32024         if (!brick_id) {
32025             return;
32026         }
32027         
32028         for (var i = 0; i<this.bricks.length; i++) {
32029             if (this.bricks[i].id == brick_id) {
32030                 this.bricks.splice(i,1);
32031                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32032                 this.initial();
32033             }
32034         }
32035     },
32036     
32037     /**
32038     * adds a Masonry Brick
32039     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32040     */
32041     addBrick : function(cfg)
32042     {
32043         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32044         //this.register(cn);
32045         cn.parentId = this.id;
32046         cn.render(this.el);
32047         return cn;
32048     },
32049     
32050     /**
32051     * register a Masonry Brick
32052     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32053     */
32054     
32055     register : function(brick)
32056     {
32057         this.bricks.push(brick);
32058         brick.masonryId = this.id;
32059     },
32060     
32061     /**
32062     * clear all the Masonry Brick
32063     */
32064     clearAll : function()
32065     {
32066         this.bricks = [];
32067         //this.getChildContainer().dom.innerHTML = "";
32068         this.el.dom.innerHTML = '';
32069     },
32070     
32071     getSelected : function()
32072     {
32073         if (!this.selectedBrick) {
32074             return false;
32075         }
32076         
32077         return this.selectedBrick;
32078     }
32079 });
32080
32081 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32082     
32083     groups: {},
32084      /**
32085     * register a Masonry Layout
32086     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32087     */
32088     
32089     register : function(layout)
32090     {
32091         this.groups[layout.id] = layout;
32092     },
32093     /**
32094     * fetch a  Masonry Layout based on the masonry layout ID
32095     * @param {string} the masonry layout to add
32096     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32097     */
32098     
32099     get: function(layout_id) {
32100         if (typeof(this.groups[layout_id]) == 'undefined') {
32101             return false;
32102         }
32103         return this.groups[layout_id] ;
32104     }
32105     
32106     
32107     
32108 });
32109
32110  
32111
32112  /**
32113  *
32114  * This is based on 
32115  * http://masonry.desandro.com
32116  *
32117  * The idea is to render all the bricks based on vertical width...
32118  *
32119  * The original code extends 'outlayer' - we might need to use that....
32120  * 
32121  */
32122
32123
32124 /**
32125  * @class Roo.bootstrap.LayoutMasonryAuto
32126  * @extends Roo.bootstrap.Component
32127  * Bootstrap Layout Masonry class
32128  * 
32129  * @constructor
32130  * Create a new Element
32131  * @param {Object} config The config object
32132  */
32133
32134 Roo.bootstrap.LayoutMasonryAuto = function(config){
32135     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32136 };
32137
32138 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32139     
32140       /**
32141      * @cfg {Boolean} isFitWidth  - resize the width..
32142      */   
32143     isFitWidth : false,  // options..
32144     /**
32145      * @cfg {Boolean} isOriginLeft = left align?
32146      */   
32147     isOriginLeft : true,
32148     /**
32149      * @cfg {Boolean} isOriginTop = top align?
32150      */   
32151     isOriginTop : false,
32152     /**
32153      * @cfg {Boolean} isLayoutInstant = no animation?
32154      */   
32155     isLayoutInstant : false, // needed?
32156     /**
32157      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32158      */   
32159     isResizingContainer : true,
32160     /**
32161      * @cfg {Number} columnWidth  width of the columns 
32162      */   
32163     
32164     columnWidth : 0,
32165     
32166     /**
32167      * @cfg {Number} maxCols maximum number of columns
32168      */   
32169     
32170     maxCols: 0,
32171     /**
32172      * @cfg {Number} padHeight padding below box..
32173      */   
32174     
32175     padHeight : 10, 
32176     
32177     /**
32178      * @cfg {Boolean} isAutoInitial defalut true
32179      */   
32180     
32181     isAutoInitial : true, 
32182     
32183     // private?
32184     gutter : 0,
32185     
32186     containerWidth: 0,
32187     initialColumnWidth : 0,
32188     currentSize : null,
32189     
32190     colYs : null, // array.
32191     maxY : 0,
32192     padWidth: 10,
32193     
32194     
32195     tag: 'div',
32196     cls: '',
32197     bricks: null, //CompositeElement
32198     cols : 0, // array?
32199     // element : null, // wrapped now this.el
32200     _isLayoutInited : null, 
32201     
32202     
32203     getAutoCreate : function(){
32204         
32205         var cfg = {
32206             tag: this.tag,
32207             cls: 'blog-masonary-wrapper ' + this.cls,
32208             cn : {
32209                 cls : 'mas-boxes masonary'
32210             }
32211         };
32212         
32213         return cfg;
32214     },
32215     
32216     getChildContainer: function( )
32217     {
32218         if (this.boxesEl) {
32219             return this.boxesEl;
32220         }
32221         
32222         this.boxesEl = this.el.select('.mas-boxes').first();
32223         
32224         return this.boxesEl;
32225     },
32226     
32227     
32228     initEvents : function()
32229     {
32230         var _this = this;
32231         
32232         if(this.isAutoInitial){
32233             Roo.log('hook children rendered');
32234             this.on('childrenrendered', function() {
32235                 Roo.log('children rendered');
32236                 _this.initial();
32237             } ,this);
32238         }
32239         
32240     },
32241     
32242     initial : function()
32243     {
32244         this.reloadItems();
32245
32246         this.currentSize = this.el.getBox(true);
32247
32248         /// was window resize... - let's see if this works..
32249         Roo.EventManager.onWindowResize(this.resize, this); 
32250
32251         if(!this.isAutoInitial){
32252             this.layout();
32253             return;
32254         }
32255         
32256         this.layout.defer(500,this);
32257     },
32258     
32259     reloadItems: function()
32260     {
32261         this.bricks = this.el.select('.masonry-brick', true);
32262         
32263         this.bricks.each(function(b) {
32264             //Roo.log(b.getSize());
32265             if (!b.attr('originalwidth')) {
32266                 b.attr('originalwidth',  b.getSize().width);
32267             }
32268             
32269         });
32270         
32271         Roo.log(this.bricks.elements.length);
32272     },
32273     
32274     resize : function()
32275     {
32276         Roo.log('resize');
32277         var cs = this.el.getBox(true);
32278         
32279         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32280             Roo.log("no change in with or X");
32281             return;
32282         }
32283         this.currentSize = cs;
32284         this.layout();
32285     },
32286     
32287     layout : function()
32288     {
32289          Roo.log('layout');
32290         this._resetLayout();
32291         //this._manageStamps();
32292       
32293         // don't animate first layout
32294         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32295         this.layoutItems( isInstant );
32296       
32297         // flag for initalized
32298         this._isLayoutInited = true;
32299     },
32300     
32301     layoutItems : function( isInstant )
32302     {
32303         //var items = this._getItemsForLayout( this.items );
32304         // original code supports filtering layout items.. we just ignore it..
32305         
32306         this._layoutItems( this.bricks , isInstant );
32307       
32308         this._postLayout();
32309     },
32310     _layoutItems : function ( items , isInstant)
32311     {
32312        //this.fireEvent( 'layout', this, items );
32313     
32314
32315         if ( !items || !items.elements.length ) {
32316           // no items, emit event with empty array
32317             return;
32318         }
32319
32320         var queue = [];
32321         items.each(function(item) {
32322             Roo.log("layout item");
32323             Roo.log(item);
32324             // get x/y object from method
32325             var position = this._getItemLayoutPosition( item );
32326             // enqueue
32327             position.item = item;
32328             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32329             queue.push( position );
32330         }, this);
32331       
32332         this._processLayoutQueue( queue );
32333     },
32334     /** Sets position of item in DOM
32335     * @param {Element} item
32336     * @param {Number} x - horizontal position
32337     * @param {Number} y - vertical position
32338     * @param {Boolean} isInstant - disables transitions
32339     */
32340     _processLayoutQueue : function( queue )
32341     {
32342         for ( var i=0, len = queue.length; i < len; i++ ) {
32343             var obj = queue[i];
32344             obj.item.position('absolute');
32345             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32346         }
32347     },
32348       
32349     
32350     /**
32351     * Any logic you want to do after each layout,
32352     * i.e. size the container
32353     */
32354     _postLayout : function()
32355     {
32356         this.resizeContainer();
32357     },
32358     
32359     resizeContainer : function()
32360     {
32361         if ( !this.isResizingContainer ) {
32362             return;
32363         }
32364         var size = this._getContainerSize();
32365         if ( size ) {
32366             this.el.setSize(size.width,size.height);
32367             this.boxesEl.setSize(size.width,size.height);
32368         }
32369     },
32370     
32371     
32372     
32373     _resetLayout : function()
32374     {
32375         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32376         this.colWidth = this.el.getWidth();
32377         //this.gutter = this.el.getWidth(); 
32378         
32379         this.measureColumns();
32380
32381         // reset column Y
32382         var i = this.cols;
32383         this.colYs = [];
32384         while (i--) {
32385             this.colYs.push( 0 );
32386         }
32387     
32388         this.maxY = 0;
32389     },
32390
32391     measureColumns : function()
32392     {
32393         this.getContainerWidth();
32394       // if columnWidth is 0, default to outerWidth of first item
32395         if ( !this.columnWidth ) {
32396             var firstItem = this.bricks.first();
32397             Roo.log(firstItem);
32398             this.columnWidth  = this.containerWidth;
32399             if (firstItem && firstItem.attr('originalwidth') ) {
32400                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32401             }
32402             // columnWidth fall back to item of first element
32403             Roo.log("set column width?");
32404                         this.initialColumnWidth = this.columnWidth  ;
32405
32406             // if first elem has no width, default to size of container
32407             
32408         }
32409         
32410         
32411         if (this.initialColumnWidth) {
32412             this.columnWidth = this.initialColumnWidth;
32413         }
32414         
32415         
32416             
32417         // column width is fixed at the top - however if container width get's smaller we should
32418         // reduce it...
32419         
32420         // this bit calcs how man columns..
32421             
32422         var columnWidth = this.columnWidth += this.gutter;
32423       
32424         // calculate columns
32425         var containerWidth = this.containerWidth + this.gutter;
32426         
32427         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32428         // fix rounding errors, typically with gutters
32429         var excess = columnWidth - containerWidth % columnWidth;
32430         
32431         
32432         // if overshoot is less than a pixel, round up, otherwise floor it
32433         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32434         cols = Math[ mathMethod ]( cols );
32435         this.cols = Math.max( cols, 1 );
32436         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32437         
32438          // padding positioning..
32439         var totalColWidth = this.cols * this.columnWidth;
32440         var padavail = this.containerWidth - totalColWidth;
32441         // so for 2 columns - we need 3 'pads'
32442         
32443         var padNeeded = (1+this.cols) * this.padWidth;
32444         
32445         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32446         
32447         this.columnWidth += padExtra
32448         //this.padWidth = Math.floor(padavail /  ( this.cols));
32449         
32450         // adjust colum width so that padding is fixed??
32451         
32452         // we have 3 columns ... total = width * 3
32453         // we have X left over... that should be used by 
32454         
32455         //if (this.expandC) {
32456             
32457         //}
32458         
32459         
32460         
32461     },
32462     
32463     getContainerWidth : function()
32464     {
32465        /* // container is parent if fit width
32466         var container = this.isFitWidth ? this.element.parentNode : this.element;
32467         // check that this.size and size are there
32468         // IE8 triggers resize on body size change, so they might not be
32469         
32470         var size = getSize( container );  //FIXME
32471         this.containerWidth = size && size.innerWidth; //FIXME
32472         */
32473          
32474         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32475         
32476     },
32477     
32478     _getItemLayoutPosition : function( item )  // what is item?
32479     {
32480         // we resize the item to our columnWidth..
32481       
32482         item.setWidth(this.columnWidth);
32483         item.autoBoxAdjust  = false;
32484         
32485         var sz = item.getSize();
32486  
32487         // how many columns does this brick span
32488         var remainder = this.containerWidth % this.columnWidth;
32489         
32490         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32491         // round if off by 1 pixel, otherwise use ceil
32492         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32493         colSpan = Math.min( colSpan, this.cols );
32494         
32495         // normally this should be '1' as we dont' currently allow multi width columns..
32496         
32497         var colGroup = this._getColGroup( colSpan );
32498         // get the minimum Y value from the columns
32499         var minimumY = Math.min.apply( Math, colGroup );
32500         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32501         
32502         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32503          
32504         // position the brick
32505         var position = {
32506             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32507             y: this.currentSize.y + minimumY + this.padHeight
32508         };
32509         
32510         Roo.log(position);
32511         // apply setHeight to necessary columns
32512         var setHeight = minimumY + sz.height + this.padHeight;
32513         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32514         
32515         var setSpan = this.cols + 1 - colGroup.length;
32516         for ( var i = 0; i < setSpan; i++ ) {
32517           this.colYs[ shortColIndex + i ] = setHeight ;
32518         }
32519       
32520         return position;
32521     },
32522     
32523     /**
32524      * @param {Number} colSpan - number of columns the element spans
32525      * @returns {Array} colGroup
32526      */
32527     _getColGroup : function( colSpan )
32528     {
32529         if ( colSpan < 2 ) {
32530           // if brick spans only one column, use all the column Ys
32531           return this.colYs;
32532         }
32533       
32534         var colGroup = [];
32535         // how many different places could this brick fit horizontally
32536         var groupCount = this.cols + 1 - colSpan;
32537         // for each group potential horizontal position
32538         for ( var i = 0; i < groupCount; i++ ) {
32539           // make an array of colY values for that one group
32540           var groupColYs = this.colYs.slice( i, i + colSpan );
32541           // and get the max value of the array
32542           colGroup[i] = Math.max.apply( Math, groupColYs );
32543         }
32544         return colGroup;
32545     },
32546     /*
32547     _manageStamp : function( stamp )
32548     {
32549         var stampSize =  stamp.getSize();
32550         var offset = stamp.getBox();
32551         // get the columns that this stamp affects
32552         var firstX = this.isOriginLeft ? offset.x : offset.right;
32553         var lastX = firstX + stampSize.width;
32554         var firstCol = Math.floor( firstX / this.columnWidth );
32555         firstCol = Math.max( 0, firstCol );
32556         
32557         var lastCol = Math.floor( lastX / this.columnWidth );
32558         // lastCol should not go over if multiple of columnWidth #425
32559         lastCol -= lastX % this.columnWidth ? 0 : 1;
32560         lastCol = Math.min( this.cols - 1, lastCol );
32561         
32562         // set colYs to bottom of the stamp
32563         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32564             stampSize.height;
32565             
32566         for ( var i = firstCol; i <= lastCol; i++ ) {
32567           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32568         }
32569     },
32570     */
32571     
32572     _getContainerSize : function()
32573     {
32574         this.maxY = Math.max.apply( Math, this.colYs );
32575         var size = {
32576             height: this.maxY
32577         };
32578       
32579         if ( this.isFitWidth ) {
32580             size.width = this._getContainerFitWidth();
32581         }
32582       
32583         return size;
32584     },
32585     
32586     _getContainerFitWidth : function()
32587     {
32588         var unusedCols = 0;
32589         // count unused columns
32590         var i = this.cols;
32591         while ( --i ) {
32592           if ( this.colYs[i] !== 0 ) {
32593             break;
32594           }
32595           unusedCols++;
32596         }
32597         // fit container to columns that have been used
32598         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32599     },
32600     
32601     needsResizeLayout : function()
32602     {
32603         var previousWidth = this.containerWidth;
32604         this.getContainerWidth();
32605         return previousWidth !== this.containerWidth;
32606     }
32607  
32608 });
32609
32610  
32611
32612  /*
32613  * - LGPL
32614  *
32615  * element
32616  * 
32617  */
32618
32619 /**
32620  * @class Roo.bootstrap.MasonryBrick
32621  * @extends Roo.bootstrap.Component
32622  * Bootstrap MasonryBrick class
32623  * 
32624  * @constructor
32625  * Create a new MasonryBrick
32626  * @param {Object} config The config object
32627  */
32628
32629 Roo.bootstrap.MasonryBrick = function(config){
32630     
32631     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32632     
32633     Roo.bootstrap.MasonryBrick.register(this);
32634     
32635     this.addEvents({
32636         // raw events
32637         /**
32638          * @event click
32639          * When a MasonryBrick is clcik
32640          * @param {Roo.bootstrap.MasonryBrick} this
32641          * @param {Roo.EventObject} e
32642          */
32643         "click" : true
32644     });
32645 };
32646
32647 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32648     
32649     /**
32650      * @cfg {String} title
32651      */   
32652     title : '',
32653     /**
32654      * @cfg {String} html
32655      */   
32656     html : '',
32657     /**
32658      * @cfg {String} bgimage
32659      */   
32660     bgimage : '',
32661     /**
32662      * @cfg {String} videourl
32663      */   
32664     videourl : '',
32665     /**
32666      * @cfg {String} cls
32667      */   
32668     cls : '',
32669     /**
32670      * @cfg {String} href
32671      */   
32672     href : '',
32673     /**
32674      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32675      */   
32676     size : 'xs',
32677     
32678     /**
32679      * @cfg {String} placetitle (center|bottom)
32680      */   
32681     placetitle : '',
32682     
32683     /**
32684      * @cfg {Boolean} isFitContainer defalut true
32685      */   
32686     isFitContainer : true, 
32687     
32688     /**
32689      * @cfg {Boolean} preventDefault defalut false
32690      */   
32691     preventDefault : false, 
32692     
32693     /**
32694      * @cfg {Boolean} inverse defalut false
32695      */   
32696     maskInverse : false, 
32697     
32698     getAutoCreate : function()
32699     {
32700         if(!this.isFitContainer){
32701             return this.getSplitAutoCreate();
32702         }
32703         
32704         var cls = 'masonry-brick masonry-brick-full';
32705         
32706         if(this.href.length){
32707             cls += ' masonry-brick-link';
32708         }
32709         
32710         if(this.bgimage.length){
32711             cls += ' masonry-brick-image';
32712         }
32713         
32714         if(this.maskInverse){
32715             cls += ' mask-inverse';
32716         }
32717         
32718         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32719             cls += ' enable-mask';
32720         }
32721         
32722         if(this.size){
32723             cls += ' masonry-' + this.size + '-brick';
32724         }
32725         
32726         if(this.placetitle.length){
32727             
32728             switch (this.placetitle) {
32729                 case 'center' :
32730                     cls += ' masonry-center-title';
32731                     break;
32732                 case 'bottom' :
32733                     cls += ' masonry-bottom-title';
32734                     break;
32735                 default:
32736                     break;
32737             }
32738             
32739         } else {
32740             if(!this.html.length && !this.bgimage.length){
32741                 cls += ' masonry-center-title';
32742             }
32743
32744             if(!this.html.length && this.bgimage.length){
32745                 cls += ' masonry-bottom-title';
32746             }
32747         }
32748         
32749         if(this.cls){
32750             cls += ' ' + this.cls;
32751         }
32752         
32753         var cfg = {
32754             tag: (this.href.length) ? 'a' : 'div',
32755             cls: cls,
32756             cn: [
32757                 {
32758                     tag: 'div',
32759                     cls: 'masonry-brick-mask'
32760                 },
32761                 {
32762                     tag: 'div',
32763                     cls: 'masonry-brick-paragraph',
32764                     cn: []
32765                 }
32766             ]
32767         };
32768         
32769         if(this.href.length){
32770             cfg.href = this.href;
32771         }
32772         
32773         var cn = cfg.cn[1].cn;
32774         
32775         if(this.title.length){
32776             cn.push({
32777                 tag: 'h4',
32778                 cls: 'masonry-brick-title',
32779                 html: this.title
32780             });
32781         }
32782         
32783         if(this.html.length){
32784             cn.push({
32785                 tag: 'p',
32786                 cls: 'masonry-brick-text',
32787                 html: this.html
32788             });
32789         }
32790         
32791         if (!this.title.length && !this.html.length) {
32792             cfg.cn[1].cls += ' hide';
32793         }
32794         
32795         if(this.bgimage.length){
32796             cfg.cn.push({
32797                 tag: 'img',
32798                 cls: 'masonry-brick-image-view',
32799                 src: this.bgimage
32800             });
32801         }
32802         
32803         if(this.videourl.length){
32804             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32805             // youtube support only?
32806             cfg.cn.push({
32807                 tag: 'iframe',
32808                 cls: 'masonry-brick-image-view',
32809                 src: vurl,
32810                 frameborder : 0,
32811                 allowfullscreen : true
32812             });
32813         }
32814         
32815         return cfg;
32816         
32817     },
32818     
32819     getSplitAutoCreate : function()
32820     {
32821         var cls = 'masonry-brick masonry-brick-split';
32822         
32823         if(this.href.length){
32824             cls += ' masonry-brick-link';
32825         }
32826         
32827         if(this.bgimage.length){
32828             cls += ' masonry-brick-image';
32829         }
32830         
32831         if(this.size){
32832             cls += ' masonry-' + this.size + '-brick';
32833         }
32834         
32835         switch (this.placetitle) {
32836             case 'center' :
32837                 cls += ' masonry-center-title';
32838                 break;
32839             case 'bottom' :
32840                 cls += ' masonry-bottom-title';
32841                 break;
32842             default:
32843                 if(!this.bgimage.length){
32844                     cls += ' masonry-center-title';
32845                 }
32846
32847                 if(this.bgimage.length){
32848                     cls += ' masonry-bottom-title';
32849                 }
32850                 break;
32851         }
32852         
32853         if(this.cls){
32854             cls += ' ' + this.cls;
32855         }
32856         
32857         var cfg = {
32858             tag: (this.href.length) ? 'a' : 'div',
32859             cls: cls,
32860             cn: [
32861                 {
32862                     tag: 'div',
32863                     cls: 'masonry-brick-split-head',
32864                     cn: [
32865                         {
32866                             tag: 'div',
32867                             cls: 'masonry-brick-paragraph',
32868                             cn: []
32869                         }
32870                     ]
32871                 },
32872                 {
32873                     tag: 'div',
32874                     cls: 'masonry-brick-split-body',
32875                     cn: []
32876                 }
32877             ]
32878         };
32879         
32880         if(this.href.length){
32881             cfg.href = this.href;
32882         }
32883         
32884         if(this.title.length){
32885             cfg.cn[0].cn[0].cn.push({
32886                 tag: 'h4',
32887                 cls: 'masonry-brick-title',
32888                 html: this.title
32889             });
32890         }
32891         
32892         if(this.html.length){
32893             cfg.cn[1].cn.push({
32894                 tag: 'p',
32895                 cls: 'masonry-brick-text',
32896                 html: this.html
32897             });
32898         }
32899
32900         if(this.bgimage.length){
32901             cfg.cn[0].cn.push({
32902                 tag: 'img',
32903                 cls: 'masonry-brick-image-view',
32904                 src: this.bgimage
32905             });
32906         }
32907         
32908         if(this.videourl.length){
32909             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32910             // youtube support only?
32911             cfg.cn[0].cn.cn.push({
32912                 tag: 'iframe',
32913                 cls: 'masonry-brick-image-view',
32914                 src: vurl,
32915                 frameborder : 0,
32916                 allowfullscreen : true
32917             });
32918         }
32919         
32920         return cfg;
32921     },
32922     
32923     initEvents: function() 
32924     {
32925         switch (this.size) {
32926             case 'xs' :
32927                 this.x = 1;
32928                 this.y = 1;
32929                 break;
32930             case 'sm' :
32931                 this.x = 2;
32932                 this.y = 2;
32933                 break;
32934             case 'md' :
32935             case 'md-left' :
32936             case 'md-right' :
32937                 this.x = 3;
32938                 this.y = 3;
32939                 break;
32940             case 'tall' :
32941                 this.x = 2;
32942                 this.y = 3;
32943                 break;
32944             case 'wide' :
32945                 this.x = 3;
32946                 this.y = 2;
32947                 break;
32948             case 'wide-thin' :
32949                 this.x = 3;
32950                 this.y = 1;
32951                 break;
32952                         
32953             default :
32954                 break;
32955         }
32956         
32957         if(Roo.isTouch){
32958             this.el.on('touchstart', this.onTouchStart, this);
32959             this.el.on('touchmove', this.onTouchMove, this);
32960             this.el.on('touchend', this.onTouchEnd, this);
32961             this.el.on('contextmenu', this.onContextMenu, this);
32962         } else {
32963             this.el.on('mouseenter'  ,this.enter, this);
32964             this.el.on('mouseleave', this.leave, this);
32965             this.el.on('click', this.onClick, this);
32966         }
32967         
32968         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32969             this.parent().bricks.push(this);   
32970         }
32971         
32972     },
32973     
32974     onClick: function(e, el)
32975     {
32976         var time = this.endTimer - this.startTimer;
32977         // Roo.log(e.preventDefault());
32978         if(Roo.isTouch){
32979             if(time > 1000){
32980                 e.preventDefault();
32981                 return;
32982             }
32983         }
32984         
32985         if(!this.preventDefault){
32986             return;
32987         }
32988         
32989         e.preventDefault();
32990         
32991         if (this.activeClass != '') {
32992             this.selectBrick();
32993         }
32994         
32995         this.fireEvent('click', this, e);
32996     },
32997     
32998     enter: function(e, el)
32999     {
33000         e.preventDefault();
33001         
33002         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33003             return;
33004         }
33005         
33006         if(this.bgimage.length && this.html.length){
33007             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33008         }
33009     },
33010     
33011     leave: function(e, el)
33012     {
33013         e.preventDefault();
33014         
33015         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33016             return;
33017         }
33018         
33019         if(this.bgimage.length && this.html.length){
33020             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33021         }
33022     },
33023     
33024     onTouchStart: function(e, el)
33025     {
33026 //        e.preventDefault();
33027         
33028         this.touchmoved = false;
33029         
33030         if(!this.isFitContainer){
33031             return;
33032         }
33033         
33034         if(!this.bgimage.length || !this.html.length){
33035             return;
33036         }
33037         
33038         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33039         
33040         this.timer = new Date().getTime();
33041         
33042     },
33043     
33044     onTouchMove: function(e, el)
33045     {
33046         this.touchmoved = true;
33047     },
33048     
33049     onContextMenu : function(e,el)
33050     {
33051         e.preventDefault();
33052         e.stopPropagation();
33053         return false;
33054     },
33055     
33056     onTouchEnd: function(e, el)
33057     {
33058 //        e.preventDefault();
33059         
33060         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33061         
33062             this.leave(e,el);
33063             
33064             return;
33065         }
33066         
33067         if(!this.bgimage.length || !this.html.length){
33068             
33069             if(this.href.length){
33070                 window.location.href = this.href;
33071             }
33072             
33073             return;
33074         }
33075         
33076         if(!this.isFitContainer){
33077             return;
33078         }
33079         
33080         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33081         
33082         window.location.href = this.href;
33083     },
33084     
33085     //selection on single brick only
33086     selectBrick : function() {
33087         
33088         if (!this.parentId) {
33089             return;
33090         }
33091         
33092         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33093         var index = m.selectedBrick.indexOf(this.id);
33094         
33095         if ( index > -1) {
33096             m.selectedBrick.splice(index,1);
33097             this.el.removeClass(this.activeClass);
33098             return;
33099         }
33100         
33101         for(var i = 0; i < m.selectedBrick.length; i++) {
33102             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33103             b.el.removeClass(b.activeClass);
33104         }
33105         
33106         m.selectedBrick = [];
33107         
33108         m.selectedBrick.push(this.id);
33109         this.el.addClass(this.activeClass);
33110         return;
33111     },
33112     
33113     isSelected : function(){
33114         return this.el.hasClass(this.activeClass);
33115         
33116     }
33117 });
33118
33119 Roo.apply(Roo.bootstrap.MasonryBrick, {
33120     
33121     //groups: {},
33122     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33123      /**
33124     * register a Masonry Brick
33125     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33126     */
33127     
33128     register : function(brick)
33129     {
33130         //this.groups[brick.id] = brick;
33131         this.groups.add(brick.id, brick);
33132     },
33133     /**
33134     * fetch a  masonry brick based on the masonry brick ID
33135     * @param {string} the masonry brick to add
33136     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33137     */
33138     
33139     get: function(brick_id) 
33140     {
33141         // if (typeof(this.groups[brick_id]) == 'undefined') {
33142         //     return false;
33143         // }
33144         // return this.groups[brick_id] ;
33145         
33146         if(this.groups.key(brick_id)) {
33147             return this.groups.key(brick_id);
33148         }
33149         
33150         return false;
33151     }
33152     
33153     
33154     
33155 });
33156
33157  /*
33158  * - LGPL
33159  *
33160  * element
33161  * 
33162  */
33163
33164 /**
33165  * @class Roo.bootstrap.Brick
33166  * @extends Roo.bootstrap.Component
33167  * Bootstrap Brick class
33168  * 
33169  * @constructor
33170  * Create a new Brick
33171  * @param {Object} config The config object
33172  */
33173
33174 Roo.bootstrap.Brick = function(config){
33175     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33176     
33177     this.addEvents({
33178         // raw events
33179         /**
33180          * @event click
33181          * When a Brick is click
33182          * @param {Roo.bootstrap.Brick} this
33183          * @param {Roo.EventObject} e
33184          */
33185         "click" : true
33186     });
33187 };
33188
33189 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33190     
33191     /**
33192      * @cfg {String} title
33193      */   
33194     title : '',
33195     /**
33196      * @cfg {String} html
33197      */   
33198     html : '',
33199     /**
33200      * @cfg {String} bgimage
33201      */   
33202     bgimage : '',
33203     /**
33204      * @cfg {String} cls
33205      */   
33206     cls : '',
33207     /**
33208      * @cfg {String} href
33209      */   
33210     href : '',
33211     /**
33212      * @cfg {String} video
33213      */   
33214     video : '',
33215     /**
33216      * @cfg {Boolean} square
33217      */   
33218     square : true,
33219     
33220     getAutoCreate : function()
33221     {
33222         var cls = 'roo-brick';
33223         
33224         if(this.href.length){
33225             cls += ' roo-brick-link';
33226         }
33227         
33228         if(this.bgimage.length){
33229             cls += ' roo-brick-image';
33230         }
33231         
33232         if(!this.html.length && !this.bgimage.length){
33233             cls += ' roo-brick-center-title';
33234         }
33235         
33236         if(!this.html.length && this.bgimage.length){
33237             cls += ' roo-brick-bottom-title';
33238         }
33239         
33240         if(this.cls){
33241             cls += ' ' + this.cls;
33242         }
33243         
33244         var cfg = {
33245             tag: (this.href.length) ? 'a' : 'div',
33246             cls: cls,
33247             cn: [
33248                 {
33249                     tag: 'div',
33250                     cls: 'roo-brick-paragraph',
33251                     cn: []
33252                 }
33253             ]
33254         };
33255         
33256         if(this.href.length){
33257             cfg.href = this.href;
33258         }
33259         
33260         var cn = cfg.cn[0].cn;
33261         
33262         if(this.title.length){
33263             cn.push({
33264                 tag: 'h4',
33265                 cls: 'roo-brick-title',
33266                 html: this.title
33267             });
33268         }
33269         
33270         if(this.html.length){
33271             cn.push({
33272                 tag: 'p',
33273                 cls: 'roo-brick-text',
33274                 html: this.html
33275             });
33276         } else {
33277             cn.cls += ' hide';
33278         }
33279         
33280         if(this.bgimage.length){
33281             cfg.cn.push({
33282                 tag: 'img',
33283                 cls: 'roo-brick-image-view',
33284                 src: this.bgimage
33285             });
33286         }
33287         
33288         return cfg;
33289     },
33290     
33291     initEvents: function() 
33292     {
33293         if(this.title.length || this.html.length){
33294             this.el.on('mouseenter'  ,this.enter, this);
33295             this.el.on('mouseleave', this.leave, this);
33296         }
33297         
33298         Roo.EventManager.onWindowResize(this.resize, this); 
33299         
33300         if(this.bgimage.length){
33301             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33302             this.imageEl.on('load', this.onImageLoad, this);
33303             return;
33304         }
33305         
33306         this.resize();
33307     },
33308     
33309     onImageLoad : function()
33310     {
33311         this.resize();
33312     },
33313     
33314     resize : function()
33315     {
33316         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33317         
33318         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33319         
33320         if(this.bgimage.length){
33321             var image = this.el.select('.roo-brick-image-view', true).first();
33322             
33323             image.setWidth(paragraph.getWidth());
33324             
33325             if(this.square){
33326                 image.setHeight(paragraph.getWidth());
33327             }
33328             
33329             this.el.setHeight(image.getHeight());
33330             paragraph.setHeight(image.getHeight());
33331             
33332         }
33333         
33334     },
33335     
33336     enter: function(e, el)
33337     {
33338         e.preventDefault();
33339         
33340         if(this.bgimage.length){
33341             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33342             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33343         }
33344     },
33345     
33346     leave: function(e, el)
33347     {
33348         e.preventDefault();
33349         
33350         if(this.bgimage.length){
33351             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33352             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33353         }
33354     }
33355     
33356 });
33357
33358  
33359
33360  /*
33361  * - LGPL
33362  *
33363  * Number field 
33364  */
33365
33366 /**
33367  * @class Roo.bootstrap.NumberField
33368  * @extends Roo.bootstrap.Input
33369  * Bootstrap NumberField class
33370  * 
33371  * 
33372  * 
33373  * 
33374  * @constructor
33375  * Create a new NumberField
33376  * @param {Object} config The config object
33377  */
33378
33379 Roo.bootstrap.NumberField = function(config){
33380     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33381 };
33382
33383 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33384     
33385     /**
33386      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33387      */
33388     allowDecimals : true,
33389     /**
33390      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33391      */
33392     decimalSeparator : ".",
33393     /**
33394      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33395      */
33396     decimalPrecision : 2,
33397     /**
33398      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33399      */
33400     allowNegative : true,
33401     
33402     /**
33403      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33404      */
33405     allowZero: true,
33406     /**
33407      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33408      */
33409     minValue : Number.NEGATIVE_INFINITY,
33410     /**
33411      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33412      */
33413     maxValue : Number.MAX_VALUE,
33414     /**
33415      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33416      */
33417     minText : "The minimum value for this field is {0}",
33418     /**
33419      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33420      */
33421     maxText : "The maximum value for this field is {0}",
33422     /**
33423      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33424      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33425      */
33426     nanText : "{0} is not a valid number",
33427     /**
33428      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33429      */
33430     thousandsDelimiter : false,
33431     /**
33432      * @cfg {String} valueAlign alignment of value
33433      */
33434     valueAlign : "left",
33435
33436     getAutoCreate : function()
33437     {
33438         var hiddenInput = {
33439             tag: 'input',
33440             type: 'hidden',
33441             id: Roo.id(),
33442             cls: 'hidden-number-input'
33443         };
33444         
33445         if (this.name) {
33446             hiddenInput.name = this.name;
33447         }
33448         
33449         this.name = '';
33450         
33451         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33452         
33453         this.name = hiddenInput.name;
33454         
33455         if(cfg.cn.length > 0) {
33456             cfg.cn.push(hiddenInput);
33457         }
33458         
33459         return cfg;
33460     },
33461
33462     // private
33463     initEvents : function()
33464     {   
33465         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33466         
33467         var allowed = "0123456789";
33468         
33469         if(this.allowDecimals){
33470             allowed += this.decimalSeparator;
33471         }
33472         
33473         if(this.allowNegative){
33474             allowed += "-";
33475         }
33476         
33477         if(this.thousandsDelimiter) {
33478             allowed += ",";
33479         }
33480         
33481         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33482         
33483         var keyPress = function(e){
33484             
33485             var k = e.getKey();
33486             
33487             var c = e.getCharCode();
33488             
33489             if(
33490                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33491                     allowed.indexOf(String.fromCharCode(c)) === -1
33492             ){
33493                 e.stopEvent();
33494                 return;
33495             }
33496             
33497             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33498                 return;
33499             }
33500             
33501             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33502                 e.stopEvent();
33503             }
33504         };
33505         
33506         this.el.on("keypress", keyPress, this);
33507     },
33508     
33509     validateValue : function(value)
33510     {
33511         
33512         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33513             return false;
33514         }
33515         
33516         var num = this.parseValue(value);
33517         
33518         if(isNaN(num)){
33519             this.markInvalid(String.format(this.nanText, value));
33520             return false;
33521         }
33522         
33523         if(num < this.minValue){
33524             this.markInvalid(String.format(this.minText, this.minValue));
33525             return false;
33526         }
33527         
33528         if(num > this.maxValue){
33529             this.markInvalid(String.format(this.maxText, this.maxValue));
33530             return false;
33531         }
33532         
33533         return true;
33534     },
33535
33536     getValue : function()
33537     {
33538         var v = this.hiddenEl().getValue();
33539         
33540         return this.fixPrecision(this.parseValue(v));
33541     },
33542
33543     parseValue : function(value)
33544     {
33545         if(this.thousandsDelimiter) {
33546             value += "";
33547             r = new RegExp(",", "g");
33548             value = value.replace(r, "");
33549         }
33550         
33551         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33552         return isNaN(value) ? '' : value;
33553     },
33554
33555     fixPrecision : function(value)
33556     {
33557         if(this.thousandsDelimiter) {
33558             value += "";
33559             r = new RegExp(",", "g");
33560             value = value.replace(r, "");
33561         }
33562         
33563         var nan = isNaN(value);
33564         
33565         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33566             return nan ? '' : value;
33567         }
33568         return parseFloat(value).toFixed(this.decimalPrecision);
33569     },
33570
33571     setValue : function(v)
33572     {
33573         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33574         
33575         this.value = v;
33576         
33577         if(this.rendered){
33578             
33579             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33580             
33581             this.inputEl().dom.value = (v == '') ? '' :
33582                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33583             
33584             if(!this.allowZero && v === '0') {
33585                 this.hiddenEl().dom.value = '';
33586                 this.inputEl().dom.value = '';
33587             }
33588             
33589             this.validate();
33590         }
33591     },
33592
33593     decimalPrecisionFcn : function(v)
33594     {
33595         return Math.floor(v);
33596     },
33597
33598     beforeBlur : function()
33599     {
33600         var v = this.parseValue(this.getRawValue());
33601         
33602         if(v || v === 0 || v === ''){
33603             this.setValue(v);
33604         }
33605     },
33606     
33607     hiddenEl : function()
33608     {
33609         return this.el.select('input.hidden-number-input',true).first();
33610     }
33611     
33612 });
33613
33614  
33615
33616 /*
33617 * Licence: LGPL
33618 */
33619
33620 /**
33621  * @class Roo.bootstrap.DocumentSlider
33622  * @extends Roo.bootstrap.Component
33623  * Bootstrap DocumentSlider class
33624  * 
33625  * @constructor
33626  * Create a new DocumentViewer
33627  * @param {Object} config The config object
33628  */
33629
33630 Roo.bootstrap.DocumentSlider = function(config){
33631     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33632     
33633     this.files = [];
33634     
33635     this.addEvents({
33636         /**
33637          * @event initial
33638          * Fire after initEvent
33639          * @param {Roo.bootstrap.DocumentSlider} this
33640          */
33641         "initial" : true,
33642         /**
33643          * @event update
33644          * Fire after update
33645          * @param {Roo.bootstrap.DocumentSlider} this
33646          */
33647         "update" : true,
33648         /**
33649          * @event click
33650          * Fire after click
33651          * @param {Roo.bootstrap.DocumentSlider} this
33652          */
33653         "click" : true
33654     });
33655 };
33656
33657 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33658     
33659     files : false,
33660     
33661     indicator : 0,
33662     
33663     getAutoCreate : function()
33664     {
33665         var cfg = {
33666             tag : 'div',
33667             cls : 'roo-document-slider',
33668             cn : [
33669                 {
33670                     tag : 'div',
33671                     cls : 'roo-document-slider-header',
33672                     cn : [
33673                         {
33674                             tag : 'div',
33675                             cls : 'roo-document-slider-header-title'
33676                         }
33677                     ]
33678                 },
33679                 {
33680                     tag : 'div',
33681                     cls : 'roo-document-slider-body',
33682                     cn : [
33683                         {
33684                             tag : 'div',
33685                             cls : 'roo-document-slider-prev',
33686                             cn : [
33687                                 {
33688                                     tag : 'i',
33689                                     cls : 'fa fa-chevron-left'
33690                                 }
33691                             ]
33692                         },
33693                         {
33694                             tag : 'div',
33695                             cls : 'roo-document-slider-thumb',
33696                             cn : [
33697                                 {
33698                                     tag : 'img',
33699                                     cls : 'roo-document-slider-image'
33700                                 }
33701                             ]
33702                         },
33703                         {
33704                             tag : 'div',
33705                             cls : 'roo-document-slider-next',
33706                             cn : [
33707                                 {
33708                                     tag : 'i',
33709                                     cls : 'fa fa-chevron-right'
33710                                 }
33711                             ]
33712                         }
33713                     ]
33714                 }
33715             ]
33716         };
33717         
33718         return cfg;
33719     },
33720     
33721     initEvents : function()
33722     {
33723         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33724         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33725         
33726         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33727         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33728         
33729         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33730         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33731         
33732         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33733         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33734         
33735         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33736         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33737         
33738         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33739         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33740         
33741         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33742         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33743         
33744         this.thumbEl.on('click', this.onClick, this);
33745         
33746         this.prevIndicator.on('click', this.prev, this);
33747         
33748         this.nextIndicator.on('click', this.next, this);
33749         
33750     },
33751     
33752     initial : function()
33753     {
33754         if(this.files.length){
33755             this.indicator = 1;
33756             this.update()
33757         }
33758         
33759         this.fireEvent('initial', this);
33760     },
33761     
33762     update : function()
33763     {
33764         this.imageEl.attr('src', this.files[this.indicator - 1]);
33765         
33766         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33767         
33768         this.prevIndicator.show();
33769         
33770         if(this.indicator == 1){
33771             this.prevIndicator.hide();
33772         }
33773         
33774         this.nextIndicator.show();
33775         
33776         if(this.indicator == this.files.length){
33777             this.nextIndicator.hide();
33778         }
33779         
33780         this.thumbEl.scrollTo('top');
33781         
33782         this.fireEvent('update', this);
33783     },
33784     
33785     onClick : function(e)
33786     {
33787         e.preventDefault();
33788         
33789         this.fireEvent('click', this);
33790     },
33791     
33792     prev : function(e)
33793     {
33794         e.preventDefault();
33795         
33796         this.indicator = Math.max(1, this.indicator - 1);
33797         
33798         this.update();
33799     },
33800     
33801     next : function(e)
33802     {
33803         e.preventDefault();
33804         
33805         this.indicator = Math.min(this.files.length, this.indicator + 1);
33806         
33807         this.update();
33808     }
33809 });
33810 /*
33811  * - LGPL
33812  *
33813  * RadioSet
33814  *
33815  *
33816  */
33817
33818 /**
33819  * @class Roo.bootstrap.RadioSet
33820  * @extends Roo.bootstrap.Input
33821  * Bootstrap RadioSet class
33822  * @cfg {String} indicatorpos (left|right) default left
33823  * @cfg {Boolean} inline (true|false) inline the element (default true)
33824  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33825  * @constructor
33826  * Create a new RadioSet
33827  * @param {Object} config The config object
33828  */
33829
33830 Roo.bootstrap.RadioSet = function(config){
33831     
33832     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33833     
33834     this.radioes = [];
33835     
33836     Roo.bootstrap.RadioSet.register(this);
33837     
33838     this.addEvents({
33839         /**
33840         * @event check
33841         * Fires when the element is checked or unchecked.
33842         * @param {Roo.bootstrap.RadioSet} this This radio
33843         * @param {Roo.bootstrap.Radio} item The checked item
33844         */
33845        check : true,
33846        /**
33847         * @event click
33848         * Fires when the element is click.
33849         * @param {Roo.bootstrap.RadioSet} this This radio set
33850         * @param {Roo.bootstrap.Radio} item The checked item
33851         * @param {Roo.EventObject} e The event object
33852         */
33853        click : true
33854     });
33855     
33856 };
33857
33858 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33859
33860     radioes : false,
33861     
33862     inline : true,
33863     
33864     weight : '',
33865     
33866     indicatorpos : 'left',
33867     
33868     getAutoCreate : function()
33869     {
33870         var label = {
33871             tag : 'label',
33872             cls : 'roo-radio-set-label',
33873             cn : [
33874                 {
33875                     tag : 'span',
33876                     html : this.fieldLabel
33877                 }
33878             ]
33879         };
33880         
33881         if(this.indicatorpos == 'left'){
33882             label.cn.unshift({
33883                 tag : 'i',
33884                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33885                 tooltip : 'This field is required'
33886             });
33887         } else {
33888             label.cn.push({
33889                 tag : 'i',
33890                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33891                 tooltip : 'This field is required'
33892             });
33893         }
33894         
33895         var items = {
33896             tag : 'div',
33897             cls : 'roo-radio-set-items'
33898         };
33899         
33900         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33901         
33902         if (align === 'left' && this.fieldLabel.length) {
33903             
33904             items = {
33905                 cls : "roo-radio-set-right", 
33906                 cn: [
33907                     items
33908                 ]
33909             };
33910             
33911             if(this.labelWidth > 12){
33912                 label.style = "width: " + this.labelWidth + 'px';
33913             }
33914             
33915             if(this.labelWidth < 13 && this.labelmd == 0){
33916                 this.labelmd = this.labelWidth;
33917             }
33918             
33919             if(this.labellg > 0){
33920                 label.cls += ' col-lg-' + this.labellg;
33921                 items.cls += ' col-lg-' + (12 - this.labellg);
33922             }
33923             
33924             if(this.labelmd > 0){
33925                 label.cls += ' col-md-' + this.labelmd;
33926                 items.cls += ' col-md-' + (12 - this.labelmd);
33927             }
33928             
33929             if(this.labelsm > 0){
33930                 label.cls += ' col-sm-' + this.labelsm;
33931                 items.cls += ' col-sm-' + (12 - this.labelsm);
33932             }
33933             
33934             if(this.labelxs > 0){
33935                 label.cls += ' col-xs-' + this.labelxs;
33936                 items.cls += ' col-xs-' + (12 - this.labelxs);
33937             }
33938         }
33939         
33940         var cfg = {
33941             tag : 'div',
33942             cls : 'roo-radio-set',
33943             cn : [
33944                 {
33945                     tag : 'input',
33946                     cls : 'roo-radio-set-input',
33947                     type : 'hidden',
33948                     name : this.name,
33949                     value : this.value ? this.value :  ''
33950                 },
33951                 label,
33952                 items
33953             ]
33954         };
33955         
33956         if(this.weight.length){
33957             cfg.cls += ' roo-radio-' + this.weight;
33958         }
33959         
33960         if(this.inline) {
33961             cfg.cls += ' roo-radio-set-inline';
33962         }
33963         
33964         var settings=this;
33965         ['xs','sm','md','lg'].map(function(size){
33966             if (settings[size]) {
33967                 cfg.cls += ' col-' + size + '-' + settings[size];
33968             }
33969         });
33970         
33971         return cfg;
33972         
33973     },
33974
33975     initEvents : function()
33976     {
33977         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33978         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33979         
33980         if(!this.fieldLabel.length){
33981             this.labelEl.hide();
33982         }
33983         
33984         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33985         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33986         
33987         this.indicator = this.indicatorEl();
33988         
33989         if(this.indicator){
33990             this.indicator.addClass('invisible');
33991         }
33992         
33993         this.originalValue = this.getValue();
33994         
33995     },
33996     
33997     inputEl: function ()
33998     {
33999         return this.el.select('.roo-radio-set-input', true).first();
34000     },
34001     
34002     getChildContainer : function()
34003     {
34004         return this.itemsEl;
34005     },
34006     
34007     register : function(item)
34008     {
34009         this.radioes.push(item);
34010         
34011     },
34012     
34013     validate : function()
34014     {   
34015         if(this.getVisibilityEl().hasClass('hidden')){
34016             return true;
34017         }
34018         
34019         var valid = false;
34020         
34021         Roo.each(this.radioes, function(i){
34022             if(!i.checked){
34023                 return;
34024             }
34025             
34026             valid = true;
34027             return false;
34028         });
34029         
34030         if(this.allowBlank) {
34031             return true;
34032         }
34033         
34034         if(this.disabled || valid){
34035             this.markValid();
34036             return true;
34037         }
34038         
34039         this.markInvalid();
34040         return false;
34041         
34042     },
34043     
34044     markValid : function()
34045     {
34046         if(this.labelEl.isVisible(true)){
34047             this.indicatorEl().removeClass('visible');
34048             this.indicatorEl().addClass('invisible');
34049         }
34050         
34051         this.el.removeClass([this.invalidClass, this.validClass]);
34052         this.el.addClass(this.validClass);
34053         
34054         this.fireEvent('valid', this);
34055     },
34056     
34057     markInvalid : function(msg)
34058     {
34059         if(this.allowBlank || this.disabled){
34060             return;
34061         }
34062         
34063         if(this.labelEl.isVisible(true)){
34064             this.indicatorEl().removeClass('invisible');
34065             this.indicatorEl().addClass('visible');
34066         }
34067         
34068         this.el.removeClass([this.invalidClass, this.validClass]);
34069         this.el.addClass(this.invalidClass);
34070         
34071         this.fireEvent('invalid', this, msg);
34072         
34073     },
34074     
34075     setValue : function(v, suppressEvent)
34076     {   
34077         if(this.value === v){
34078             return;
34079         }
34080         
34081         this.value = v;
34082         
34083         if(this.rendered){
34084             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34085         }
34086         
34087         Roo.each(this.radioes, function(i){
34088             i.checked = false;
34089             i.el.removeClass('checked');
34090         });
34091         
34092         Roo.each(this.radioes, function(i){
34093             
34094             if(i.value === v || i.value.toString() === v.toString()){
34095                 i.checked = true;
34096                 i.el.addClass('checked');
34097                 
34098                 if(suppressEvent !== true){
34099                     this.fireEvent('check', this, i);
34100                 }
34101                 
34102                 return false;
34103             }
34104             
34105         }, this);
34106         
34107         this.validate();
34108     },
34109     
34110     clearInvalid : function(){
34111         
34112         if(!this.el || this.preventMark){
34113             return;
34114         }
34115         
34116         this.el.removeClass([this.invalidClass]);
34117         
34118         this.fireEvent('valid', this);
34119     }
34120     
34121 });
34122
34123 Roo.apply(Roo.bootstrap.RadioSet, {
34124     
34125     groups: {},
34126     
34127     register : function(set)
34128     {
34129         this.groups[set.name] = set;
34130     },
34131     
34132     get: function(name) 
34133     {
34134         if (typeof(this.groups[name]) == 'undefined') {
34135             return false;
34136         }
34137         
34138         return this.groups[name] ;
34139     }
34140     
34141 });
34142 /*
34143  * Based on:
34144  * Ext JS Library 1.1.1
34145  * Copyright(c) 2006-2007, Ext JS, LLC.
34146  *
34147  * Originally Released Under LGPL - original licence link has changed is not relivant.
34148  *
34149  * Fork - LGPL
34150  * <script type="text/javascript">
34151  */
34152
34153
34154 /**
34155  * @class Roo.bootstrap.SplitBar
34156  * @extends Roo.util.Observable
34157  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34158  * <br><br>
34159  * Usage:
34160  * <pre><code>
34161 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34162                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34163 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34164 split.minSize = 100;
34165 split.maxSize = 600;
34166 split.animate = true;
34167 split.on('moved', splitterMoved);
34168 </code></pre>
34169  * @constructor
34170  * Create a new SplitBar
34171  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34172  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34173  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34174  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34175                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34176                         position of the SplitBar).
34177  */
34178 Roo.bootstrap.SplitBar = function(cfg){
34179     
34180     /** @private */
34181     
34182     //{
34183     //  dragElement : elm
34184     //  resizingElement: el,
34185         // optional..
34186     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34187     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34188         // existingProxy ???
34189     //}
34190     
34191     this.el = Roo.get(cfg.dragElement, true);
34192     this.el.dom.unselectable = "on";
34193     /** @private */
34194     this.resizingEl = Roo.get(cfg.resizingElement, true);
34195
34196     /**
34197      * @private
34198      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34199      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34200      * @type Number
34201      */
34202     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34203     
34204     /**
34205      * The minimum size of the resizing element. (Defaults to 0)
34206      * @type Number
34207      */
34208     this.minSize = 0;
34209     
34210     /**
34211      * The maximum size of the resizing element. (Defaults to 2000)
34212      * @type Number
34213      */
34214     this.maxSize = 2000;
34215     
34216     /**
34217      * Whether to animate the transition to the new size
34218      * @type Boolean
34219      */
34220     this.animate = false;
34221     
34222     /**
34223      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34224      * @type Boolean
34225      */
34226     this.useShim = false;
34227     
34228     /** @private */
34229     this.shim = null;
34230     
34231     if(!cfg.existingProxy){
34232         /** @private */
34233         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34234     }else{
34235         this.proxy = Roo.get(cfg.existingProxy).dom;
34236     }
34237     /** @private */
34238     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34239     
34240     /** @private */
34241     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34242     
34243     /** @private */
34244     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34245     
34246     /** @private */
34247     this.dragSpecs = {};
34248     
34249     /**
34250      * @private The adapter to use to positon and resize elements
34251      */
34252     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34253     this.adapter.init(this);
34254     
34255     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34256         /** @private */
34257         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34258         this.el.addClass("roo-splitbar-h");
34259     }else{
34260         /** @private */
34261         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34262         this.el.addClass("roo-splitbar-v");
34263     }
34264     
34265     this.addEvents({
34266         /**
34267          * @event resize
34268          * Fires when the splitter is moved (alias for {@link #event-moved})
34269          * @param {Roo.bootstrap.SplitBar} this
34270          * @param {Number} newSize the new width or height
34271          */
34272         "resize" : true,
34273         /**
34274          * @event moved
34275          * Fires when the splitter is moved
34276          * @param {Roo.bootstrap.SplitBar} this
34277          * @param {Number} newSize the new width or height
34278          */
34279         "moved" : true,
34280         /**
34281          * @event beforeresize
34282          * Fires before the splitter is dragged
34283          * @param {Roo.bootstrap.SplitBar} this
34284          */
34285         "beforeresize" : true,
34286
34287         "beforeapply" : true
34288     });
34289
34290     Roo.util.Observable.call(this);
34291 };
34292
34293 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34294     onStartProxyDrag : function(x, y){
34295         this.fireEvent("beforeresize", this);
34296         if(!this.overlay){
34297             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34298             o.unselectable();
34299             o.enableDisplayMode("block");
34300             // all splitbars share the same overlay
34301             Roo.bootstrap.SplitBar.prototype.overlay = o;
34302         }
34303         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34304         this.overlay.show();
34305         Roo.get(this.proxy).setDisplayed("block");
34306         var size = this.adapter.getElementSize(this);
34307         this.activeMinSize = this.getMinimumSize();;
34308         this.activeMaxSize = this.getMaximumSize();;
34309         var c1 = size - this.activeMinSize;
34310         var c2 = Math.max(this.activeMaxSize - size, 0);
34311         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34312             this.dd.resetConstraints();
34313             this.dd.setXConstraint(
34314                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34315                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34316             );
34317             this.dd.setYConstraint(0, 0);
34318         }else{
34319             this.dd.resetConstraints();
34320             this.dd.setXConstraint(0, 0);
34321             this.dd.setYConstraint(
34322                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34323                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34324             );
34325          }
34326         this.dragSpecs.startSize = size;
34327         this.dragSpecs.startPoint = [x, y];
34328         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34329     },
34330     
34331     /** 
34332      * @private Called after the drag operation by the DDProxy
34333      */
34334     onEndProxyDrag : function(e){
34335         Roo.get(this.proxy).setDisplayed(false);
34336         var endPoint = Roo.lib.Event.getXY(e);
34337         if(this.overlay){
34338             this.overlay.hide();
34339         }
34340         var newSize;
34341         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34342             newSize = this.dragSpecs.startSize + 
34343                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34344                     endPoint[0] - this.dragSpecs.startPoint[0] :
34345                     this.dragSpecs.startPoint[0] - endPoint[0]
34346                 );
34347         }else{
34348             newSize = this.dragSpecs.startSize + 
34349                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34350                     endPoint[1] - this.dragSpecs.startPoint[1] :
34351                     this.dragSpecs.startPoint[1] - endPoint[1]
34352                 );
34353         }
34354         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34355         if(newSize != this.dragSpecs.startSize){
34356             if(this.fireEvent('beforeapply', this, newSize) !== false){
34357                 this.adapter.setElementSize(this, newSize);
34358                 this.fireEvent("moved", this, newSize);
34359                 this.fireEvent("resize", this, newSize);
34360             }
34361         }
34362     },
34363     
34364     /**
34365      * Get the adapter this SplitBar uses
34366      * @return The adapter object
34367      */
34368     getAdapter : function(){
34369         return this.adapter;
34370     },
34371     
34372     /**
34373      * Set the adapter this SplitBar uses
34374      * @param {Object} adapter A SplitBar adapter object
34375      */
34376     setAdapter : function(adapter){
34377         this.adapter = adapter;
34378         this.adapter.init(this);
34379     },
34380     
34381     /**
34382      * Gets the minimum size for the resizing element
34383      * @return {Number} The minimum size
34384      */
34385     getMinimumSize : function(){
34386         return this.minSize;
34387     },
34388     
34389     /**
34390      * Sets the minimum size for the resizing element
34391      * @param {Number} minSize The minimum size
34392      */
34393     setMinimumSize : function(minSize){
34394         this.minSize = minSize;
34395     },
34396     
34397     /**
34398      * Gets the maximum size for the resizing element
34399      * @return {Number} The maximum size
34400      */
34401     getMaximumSize : function(){
34402         return this.maxSize;
34403     },
34404     
34405     /**
34406      * Sets the maximum size for the resizing element
34407      * @param {Number} maxSize The maximum size
34408      */
34409     setMaximumSize : function(maxSize){
34410         this.maxSize = maxSize;
34411     },
34412     
34413     /**
34414      * Sets the initialize size for the resizing element
34415      * @param {Number} size The initial size
34416      */
34417     setCurrentSize : function(size){
34418         var oldAnimate = this.animate;
34419         this.animate = false;
34420         this.adapter.setElementSize(this, size);
34421         this.animate = oldAnimate;
34422     },
34423     
34424     /**
34425      * Destroy this splitbar. 
34426      * @param {Boolean} removeEl True to remove the element
34427      */
34428     destroy : function(removeEl){
34429         if(this.shim){
34430             this.shim.remove();
34431         }
34432         this.dd.unreg();
34433         this.proxy.parentNode.removeChild(this.proxy);
34434         if(removeEl){
34435             this.el.remove();
34436         }
34437     }
34438 });
34439
34440 /**
34441  * @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.
34442  */
34443 Roo.bootstrap.SplitBar.createProxy = function(dir){
34444     var proxy = new Roo.Element(document.createElement("div"));
34445     proxy.unselectable();
34446     var cls = 'roo-splitbar-proxy';
34447     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34448     document.body.appendChild(proxy.dom);
34449     return proxy.dom;
34450 };
34451
34452 /** 
34453  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34454  * Default Adapter. It assumes the splitter and resizing element are not positioned
34455  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34456  */
34457 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34458 };
34459
34460 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34461     // do nothing for now
34462     init : function(s){
34463     
34464     },
34465     /**
34466      * Called before drag operations to get the current size of the resizing element. 
34467      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34468      */
34469      getElementSize : function(s){
34470         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34471             return s.resizingEl.getWidth();
34472         }else{
34473             return s.resizingEl.getHeight();
34474         }
34475     },
34476     
34477     /**
34478      * Called after drag operations to set the size of the resizing element.
34479      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34480      * @param {Number} newSize The new size to set
34481      * @param {Function} onComplete A function to be invoked when resizing is complete
34482      */
34483     setElementSize : function(s, newSize, onComplete){
34484         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34485             if(!s.animate){
34486                 s.resizingEl.setWidth(newSize);
34487                 if(onComplete){
34488                     onComplete(s, newSize);
34489                 }
34490             }else{
34491                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34492             }
34493         }else{
34494             
34495             if(!s.animate){
34496                 s.resizingEl.setHeight(newSize);
34497                 if(onComplete){
34498                     onComplete(s, newSize);
34499                 }
34500             }else{
34501                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34502             }
34503         }
34504     }
34505 };
34506
34507 /** 
34508  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34509  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34510  * Adapter that  moves the splitter element to align with the resized sizing element. 
34511  * Used with an absolute positioned SplitBar.
34512  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34513  * document.body, make sure you assign an id to the body element.
34514  */
34515 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34516     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34517     this.container = Roo.get(container);
34518 };
34519
34520 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34521     init : function(s){
34522         this.basic.init(s);
34523     },
34524     
34525     getElementSize : function(s){
34526         return this.basic.getElementSize(s);
34527     },
34528     
34529     setElementSize : function(s, newSize, onComplete){
34530         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34531     },
34532     
34533     moveSplitter : function(s){
34534         var yes = Roo.bootstrap.SplitBar;
34535         switch(s.placement){
34536             case yes.LEFT:
34537                 s.el.setX(s.resizingEl.getRight());
34538                 break;
34539             case yes.RIGHT:
34540                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34541                 break;
34542             case yes.TOP:
34543                 s.el.setY(s.resizingEl.getBottom());
34544                 break;
34545             case yes.BOTTOM:
34546                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34547                 break;
34548         }
34549     }
34550 };
34551
34552 /**
34553  * Orientation constant - Create a vertical SplitBar
34554  * @static
34555  * @type Number
34556  */
34557 Roo.bootstrap.SplitBar.VERTICAL = 1;
34558
34559 /**
34560  * Orientation constant - Create a horizontal SplitBar
34561  * @static
34562  * @type Number
34563  */
34564 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34565
34566 /**
34567  * Placement constant - The resizing element is to the left of the splitter element
34568  * @static
34569  * @type Number
34570  */
34571 Roo.bootstrap.SplitBar.LEFT = 1;
34572
34573 /**
34574  * Placement constant - The resizing element is to the right of the splitter element
34575  * @static
34576  * @type Number
34577  */
34578 Roo.bootstrap.SplitBar.RIGHT = 2;
34579
34580 /**
34581  * Placement constant - The resizing element is positioned above the splitter element
34582  * @static
34583  * @type Number
34584  */
34585 Roo.bootstrap.SplitBar.TOP = 3;
34586
34587 /**
34588  * Placement constant - The resizing element is positioned under splitter element
34589  * @static
34590  * @type Number
34591  */
34592 Roo.bootstrap.SplitBar.BOTTOM = 4;
34593 Roo.namespace("Roo.bootstrap.layout");/*
34594  * Based on:
34595  * Ext JS Library 1.1.1
34596  * Copyright(c) 2006-2007, Ext JS, LLC.
34597  *
34598  * Originally Released Under LGPL - original licence link has changed is not relivant.
34599  *
34600  * Fork - LGPL
34601  * <script type="text/javascript">
34602  */
34603
34604 /**
34605  * @class Roo.bootstrap.layout.Manager
34606  * @extends Roo.bootstrap.Component
34607  * Base class for layout managers.
34608  */
34609 Roo.bootstrap.layout.Manager = function(config)
34610 {
34611     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34612
34613
34614
34615
34616
34617     /** false to disable window resize monitoring @type Boolean */
34618     this.monitorWindowResize = true;
34619     this.regions = {};
34620     this.addEvents({
34621         /**
34622          * @event layout
34623          * Fires when a layout is performed.
34624          * @param {Roo.LayoutManager} this
34625          */
34626         "layout" : true,
34627         /**
34628          * @event regionresized
34629          * Fires when the user resizes a region.
34630          * @param {Roo.LayoutRegion} region The resized region
34631          * @param {Number} newSize The new size (width for east/west, height for north/south)
34632          */
34633         "regionresized" : true,
34634         /**
34635          * @event regioncollapsed
34636          * Fires when a region is collapsed.
34637          * @param {Roo.LayoutRegion} region The collapsed region
34638          */
34639         "regioncollapsed" : true,
34640         /**
34641          * @event regionexpanded
34642          * Fires when a region is expanded.
34643          * @param {Roo.LayoutRegion} region The expanded region
34644          */
34645         "regionexpanded" : true
34646     });
34647     this.updating = false;
34648
34649     if (config.el) {
34650         this.el = Roo.get(config.el);
34651         this.initEvents();
34652     }
34653
34654 };
34655
34656 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34657
34658
34659     regions : null,
34660
34661     monitorWindowResize : true,
34662
34663
34664     updating : false,
34665
34666
34667     onRender : function(ct, position)
34668     {
34669         if(!this.el){
34670             this.el = Roo.get(ct);
34671             this.initEvents();
34672         }
34673         //this.fireEvent('render',this);
34674     },
34675
34676
34677     initEvents: function()
34678     {
34679
34680
34681         // ie scrollbar fix
34682         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34683             document.body.scroll = "no";
34684         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34685             this.el.position('relative');
34686         }
34687         this.id = this.el.id;
34688         this.el.addClass("roo-layout-container");
34689         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34690         if(this.el.dom != document.body ) {
34691             this.el.on('resize', this.layout,this);
34692             this.el.on('show', this.layout,this);
34693         }
34694
34695     },
34696
34697     /**
34698      * Returns true if this layout is currently being updated
34699      * @return {Boolean}
34700      */
34701     isUpdating : function(){
34702         return this.updating;
34703     },
34704
34705     /**
34706      * Suspend the LayoutManager from doing auto-layouts while
34707      * making multiple add or remove calls
34708      */
34709     beginUpdate : function(){
34710         this.updating = true;
34711     },
34712
34713     /**
34714      * Restore auto-layouts and optionally disable the manager from performing a layout
34715      * @param {Boolean} noLayout true to disable a layout update
34716      */
34717     endUpdate : function(noLayout){
34718         this.updating = false;
34719         if(!noLayout){
34720             this.layout();
34721         }
34722     },
34723
34724     layout: function(){
34725         // abstract...
34726     },
34727
34728     onRegionResized : function(region, newSize){
34729         this.fireEvent("regionresized", region, newSize);
34730         this.layout();
34731     },
34732
34733     onRegionCollapsed : function(region){
34734         this.fireEvent("regioncollapsed", region);
34735     },
34736
34737     onRegionExpanded : function(region){
34738         this.fireEvent("regionexpanded", region);
34739     },
34740
34741     /**
34742      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34743      * performs box-model adjustments.
34744      * @return {Object} The size as an object {width: (the width), height: (the height)}
34745      */
34746     getViewSize : function()
34747     {
34748         var size;
34749         if(this.el.dom != document.body){
34750             size = this.el.getSize();
34751         }else{
34752             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34753         }
34754         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34755         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34756         return size;
34757     },
34758
34759     /**
34760      * Returns the Element this layout is bound to.
34761      * @return {Roo.Element}
34762      */
34763     getEl : function(){
34764         return this.el;
34765     },
34766
34767     /**
34768      * Returns the specified region.
34769      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34770      * @return {Roo.LayoutRegion}
34771      */
34772     getRegion : function(target){
34773         return this.regions[target.toLowerCase()];
34774     },
34775
34776     onWindowResize : function(){
34777         if(this.monitorWindowResize){
34778             this.layout();
34779         }
34780     }
34781 });
34782 /*
34783  * Based on:
34784  * Ext JS Library 1.1.1
34785  * Copyright(c) 2006-2007, Ext JS, LLC.
34786  *
34787  * Originally Released Under LGPL - original licence link has changed is not relivant.
34788  *
34789  * Fork - LGPL
34790  * <script type="text/javascript">
34791  */
34792 /**
34793  * @class Roo.bootstrap.layout.Border
34794  * @extends Roo.bootstrap.layout.Manager
34795  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34796  * please see: examples/bootstrap/nested.html<br><br>
34797  
34798 <b>The container the layout is rendered into can be either the body element or any other element.
34799 If it is not the body element, the container needs to either be an absolute positioned element,
34800 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34801 the container size if it is not the body element.</b>
34802
34803 * @constructor
34804 * Create a new Border
34805 * @param {Object} config Configuration options
34806  */
34807 Roo.bootstrap.layout.Border = function(config){
34808     config = config || {};
34809     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34810     
34811     
34812     
34813     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34814         if(config[region]){
34815             config[region].region = region;
34816             this.addRegion(config[region]);
34817         }
34818     },this);
34819     
34820 };
34821
34822 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34823
34824 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34825     /**
34826      * Creates and adds a new region if it doesn't already exist.
34827      * @param {String} target The target region key (north, south, east, west or center).
34828      * @param {Object} config The regions config object
34829      * @return {BorderLayoutRegion} The new region
34830      */
34831     addRegion : function(config)
34832     {
34833         if(!this.regions[config.region]){
34834             var r = this.factory(config);
34835             this.bindRegion(r);
34836         }
34837         return this.regions[config.region];
34838     },
34839
34840     // private (kinda)
34841     bindRegion : function(r){
34842         this.regions[r.config.region] = r;
34843         
34844         r.on("visibilitychange",    this.layout, this);
34845         r.on("paneladded",          this.layout, this);
34846         r.on("panelremoved",        this.layout, this);
34847         r.on("invalidated",         this.layout, this);
34848         r.on("resized",             this.onRegionResized, this);
34849         r.on("collapsed",           this.onRegionCollapsed, this);
34850         r.on("expanded",            this.onRegionExpanded, this);
34851     },
34852
34853     /**
34854      * Performs a layout update.
34855      */
34856     layout : function()
34857     {
34858         if(this.updating) {
34859             return;
34860         }
34861         
34862         // render all the rebions if they have not been done alreayd?
34863         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34864             if(this.regions[region] && !this.regions[region].bodyEl){
34865                 this.regions[region].onRender(this.el)
34866             }
34867         },this);
34868         
34869         var size = this.getViewSize();
34870         var w = size.width;
34871         var h = size.height;
34872         var centerW = w;
34873         var centerH = h;
34874         var centerY = 0;
34875         var centerX = 0;
34876         //var x = 0, y = 0;
34877
34878         var rs = this.regions;
34879         var north = rs["north"];
34880         var south = rs["south"]; 
34881         var west = rs["west"];
34882         var east = rs["east"];
34883         var center = rs["center"];
34884         //if(this.hideOnLayout){ // not supported anymore
34885             //c.el.setStyle("display", "none");
34886         //}
34887         if(north && north.isVisible()){
34888             var b = north.getBox();
34889             var m = north.getMargins();
34890             b.width = w - (m.left+m.right);
34891             b.x = m.left;
34892             b.y = m.top;
34893             centerY = b.height + b.y + m.bottom;
34894             centerH -= centerY;
34895             north.updateBox(this.safeBox(b));
34896         }
34897         if(south && south.isVisible()){
34898             var b = south.getBox();
34899             var m = south.getMargins();
34900             b.width = w - (m.left+m.right);
34901             b.x = m.left;
34902             var totalHeight = (b.height + m.top + m.bottom);
34903             b.y = h - totalHeight + m.top;
34904             centerH -= totalHeight;
34905             south.updateBox(this.safeBox(b));
34906         }
34907         if(west && west.isVisible()){
34908             var b = west.getBox();
34909             var m = west.getMargins();
34910             b.height = centerH - (m.top+m.bottom);
34911             b.x = m.left;
34912             b.y = centerY + m.top;
34913             var totalWidth = (b.width + m.left + m.right);
34914             centerX += totalWidth;
34915             centerW -= totalWidth;
34916             west.updateBox(this.safeBox(b));
34917         }
34918         if(east && east.isVisible()){
34919             var b = east.getBox();
34920             var m = east.getMargins();
34921             b.height = centerH - (m.top+m.bottom);
34922             var totalWidth = (b.width + m.left + m.right);
34923             b.x = w - totalWidth + m.left;
34924             b.y = centerY + m.top;
34925             centerW -= totalWidth;
34926             east.updateBox(this.safeBox(b));
34927         }
34928         if(center){
34929             var m = center.getMargins();
34930             var centerBox = {
34931                 x: centerX + m.left,
34932                 y: centerY + m.top,
34933                 width: centerW - (m.left+m.right),
34934                 height: centerH - (m.top+m.bottom)
34935             };
34936             //if(this.hideOnLayout){
34937                 //center.el.setStyle("display", "block");
34938             //}
34939             center.updateBox(this.safeBox(centerBox));
34940         }
34941         this.el.repaint();
34942         this.fireEvent("layout", this);
34943     },
34944
34945     // private
34946     safeBox : function(box){
34947         box.width = Math.max(0, box.width);
34948         box.height = Math.max(0, box.height);
34949         return box;
34950     },
34951
34952     /**
34953      * Adds a ContentPanel (or subclass) to this layout.
34954      * @param {String} target The target region key (north, south, east, west or center).
34955      * @param {Roo.ContentPanel} panel The panel to add
34956      * @return {Roo.ContentPanel} The added panel
34957      */
34958     add : function(target, panel){
34959          
34960         target = target.toLowerCase();
34961         return this.regions[target].add(panel);
34962     },
34963
34964     /**
34965      * Remove a ContentPanel (or subclass) to this layout.
34966      * @param {String} target The target region key (north, south, east, west or center).
34967      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34968      * @return {Roo.ContentPanel} The removed panel
34969      */
34970     remove : function(target, panel){
34971         target = target.toLowerCase();
34972         return this.regions[target].remove(panel);
34973     },
34974
34975     /**
34976      * Searches all regions for a panel with the specified id
34977      * @param {String} panelId
34978      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34979      */
34980     findPanel : function(panelId){
34981         var rs = this.regions;
34982         for(var target in rs){
34983             if(typeof rs[target] != "function"){
34984                 var p = rs[target].getPanel(panelId);
34985                 if(p){
34986                     return p;
34987                 }
34988             }
34989         }
34990         return null;
34991     },
34992
34993     /**
34994      * Searches all regions for a panel with the specified id and activates (shows) it.
34995      * @param {String/ContentPanel} panelId The panels id or the panel itself
34996      * @return {Roo.ContentPanel} The shown panel or null
34997      */
34998     showPanel : function(panelId) {
34999       var rs = this.regions;
35000       for(var target in rs){
35001          var r = rs[target];
35002          if(typeof r != "function"){
35003             if(r.hasPanel(panelId)){
35004                return r.showPanel(panelId);
35005             }
35006          }
35007       }
35008       return null;
35009    },
35010
35011    /**
35012      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35013      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35014      */
35015    /*
35016     restoreState : function(provider){
35017         if(!provider){
35018             provider = Roo.state.Manager;
35019         }
35020         var sm = new Roo.LayoutStateManager();
35021         sm.init(this, provider);
35022     },
35023 */
35024  
35025  
35026     /**
35027      * Adds a xtype elements to the layout.
35028      * <pre><code>
35029
35030 layout.addxtype({
35031        xtype : 'ContentPanel',
35032        region: 'west',
35033        items: [ .... ]
35034    }
35035 );
35036
35037 layout.addxtype({
35038         xtype : 'NestedLayoutPanel',
35039         region: 'west',
35040         layout: {
35041            center: { },
35042            west: { }   
35043         },
35044         items : [ ... list of content panels or nested layout panels.. ]
35045    }
35046 );
35047 </code></pre>
35048      * @param {Object} cfg Xtype definition of item to add.
35049      */
35050     addxtype : function(cfg)
35051     {
35052         // basically accepts a pannel...
35053         // can accept a layout region..!?!?
35054         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35055         
35056         
35057         // theory?  children can only be panels??
35058         
35059         //if (!cfg.xtype.match(/Panel$/)) {
35060         //    return false;
35061         //}
35062         var ret = false;
35063         
35064         if (typeof(cfg.region) == 'undefined') {
35065             Roo.log("Failed to add Panel, region was not set");
35066             Roo.log(cfg);
35067             return false;
35068         }
35069         var region = cfg.region;
35070         delete cfg.region;
35071         
35072           
35073         var xitems = [];
35074         if (cfg.items) {
35075             xitems = cfg.items;
35076             delete cfg.items;
35077         }
35078         var nb = false;
35079         
35080         switch(cfg.xtype) 
35081         {
35082             case 'Content':  // ContentPanel (el, cfg)
35083             case 'Scroll':  // ContentPanel (el, cfg)
35084             case 'View': 
35085                 cfg.autoCreate = true;
35086                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35087                 //} else {
35088                 //    var el = this.el.createChild();
35089                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35090                 //}
35091                 
35092                 this.add(region, ret);
35093                 break;
35094             
35095             /*
35096             case 'TreePanel': // our new panel!
35097                 cfg.el = this.el.createChild();
35098                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35099                 this.add(region, ret);
35100                 break;
35101             */
35102             
35103             case 'Nest': 
35104                 // create a new Layout (which is  a Border Layout...
35105                 
35106                 var clayout = cfg.layout;
35107                 clayout.el  = this.el.createChild();
35108                 clayout.items   = clayout.items  || [];
35109                 
35110                 delete cfg.layout;
35111                 
35112                 // replace this exitems with the clayout ones..
35113                 xitems = clayout.items;
35114                  
35115                 // force background off if it's in center...
35116                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35117                     cfg.background = false;
35118                 }
35119                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35120                 
35121                 
35122                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35123                 //console.log('adding nested layout panel '  + cfg.toSource());
35124                 this.add(region, ret);
35125                 nb = {}; /// find first...
35126                 break;
35127             
35128             case 'Grid':
35129                 
35130                 // needs grid and region
35131                 
35132                 //var el = this.getRegion(region).el.createChild();
35133                 /*
35134                  *var el = this.el.createChild();
35135                 // create the grid first...
35136                 cfg.grid.container = el;
35137                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35138                 */
35139                 
35140                 if (region == 'center' && this.active ) {
35141                     cfg.background = false;
35142                 }
35143                 
35144                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35145                 
35146                 this.add(region, ret);
35147                 /*
35148                 if (cfg.background) {
35149                     // render grid on panel activation (if panel background)
35150                     ret.on('activate', function(gp) {
35151                         if (!gp.grid.rendered) {
35152                     //        gp.grid.render(el);
35153                         }
35154                     });
35155                 } else {
35156                   //  cfg.grid.render(el);
35157                 }
35158                 */
35159                 break;
35160            
35161            
35162             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35163                 // it was the old xcomponent building that caused this before.
35164                 // espeically if border is the top element in the tree.
35165                 ret = this;
35166                 break; 
35167                 
35168                     
35169                 
35170                 
35171                 
35172             default:
35173                 /*
35174                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35175                     
35176                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35177                     this.add(region, ret);
35178                 } else {
35179                 */
35180                     Roo.log(cfg);
35181                     throw "Can not add '" + cfg.xtype + "' to Border";
35182                     return null;
35183              
35184                                 
35185              
35186         }
35187         this.beginUpdate();
35188         // add children..
35189         var region = '';
35190         var abn = {};
35191         Roo.each(xitems, function(i)  {
35192             region = nb && i.region ? i.region : false;
35193             
35194             var add = ret.addxtype(i);
35195            
35196             if (region) {
35197                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35198                 if (!i.background) {
35199                     abn[region] = nb[region] ;
35200                 }
35201             }
35202             
35203         });
35204         this.endUpdate();
35205
35206         // make the last non-background panel active..
35207         //if (nb) { Roo.log(abn); }
35208         if (nb) {
35209             
35210             for(var r in abn) {
35211                 region = this.getRegion(r);
35212                 if (region) {
35213                     // tried using nb[r], but it does not work..
35214                      
35215                     region.showPanel(abn[r]);
35216                    
35217                 }
35218             }
35219         }
35220         return ret;
35221         
35222     },
35223     
35224     
35225 // private
35226     factory : function(cfg)
35227     {
35228         
35229         var validRegions = Roo.bootstrap.layout.Border.regions;
35230
35231         var target = cfg.region;
35232         cfg.mgr = this;
35233         
35234         var r = Roo.bootstrap.layout;
35235         Roo.log(target);
35236         switch(target){
35237             case "north":
35238                 return new r.North(cfg);
35239             case "south":
35240                 return new r.South(cfg);
35241             case "east":
35242                 return new r.East(cfg);
35243             case "west":
35244                 return new r.West(cfg);
35245             case "center":
35246                 return new r.Center(cfg);
35247         }
35248         throw 'Layout region "'+target+'" not supported.';
35249     }
35250     
35251     
35252 });
35253  /*
35254  * Based on:
35255  * Ext JS Library 1.1.1
35256  * Copyright(c) 2006-2007, Ext JS, LLC.
35257  *
35258  * Originally Released Under LGPL - original licence link has changed is not relivant.
35259  *
35260  * Fork - LGPL
35261  * <script type="text/javascript">
35262  */
35263  
35264 /**
35265  * @class Roo.bootstrap.layout.Basic
35266  * @extends Roo.util.Observable
35267  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35268  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35269  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35270  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35271  * @cfg {string}   region  the region that it inhabits..
35272  * @cfg {bool}   skipConfig skip config?
35273  * 
35274
35275  */
35276 Roo.bootstrap.layout.Basic = function(config){
35277     
35278     this.mgr = config.mgr;
35279     
35280     this.position = config.region;
35281     
35282     var skipConfig = config.skipConfig;
35283     
35284     this.events = {
35285         /**
35286          * @scope Roo.BasicLayoutRegion
35287          */
35288         
35289         /**
35290          * @event beforeremove
35291          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35292          * @param {Roo.LayoutRegion} this
35293          * @param {Roo.ContentPanel} panel The panel
35294          * @param {Object} e The cancel event object
35295          */
35296         "beforeremove" : true,
35297         /**
35298          * @event invalidated
35299          * Fires when the layout for this region is changed.
35300          * @param {Roo.LayoutRegion} this
35301          */
35302         "invalidated" : true,
35303         /**
35304          * @event visibilitychange
35305          * Fires when this region is shown or hidden 
35306          * @param {Roo.LayoutRegion} this
35307          * @param {Boolean} visibility true or false
35308          */
35309         "visibilitychange" : true,
35310         /**
35311          * @event paneladded
35312          * Fires when a panel is added. 
35313          * @param {Roo.LayoutRegion} this
35314          * @param {Roo.ContentPanel} panel The panel
35315          */
35316         "paneladded" : true,
35317         /**
35318          * @event panelremoved
35319          * Fires when a panel is removed. 
35320          * @param {Roo.LayoutRegion} this
35321          * @param {Roo.ContentPanel} panel The panel
35322          */
35323         "panelremoved" : true,
35324         /**
35325          * @event beforecollapse
35326          * Fires when this region before collapse.
35327          * @param {Roo.LayoutRegion} this
35328          */
35329         "beforecollapse" : true,
35330         /**
35331          * @event collapsed
35332          * Fires when this region is collapsed.
35333          * @param {Roo.LayoutRegion} this
35334          */
35335         "collapsed" : true,
35336         /**
35337          * @event expanded
35338          * Fires when this region is expanded.
35339          * @param {Roo.LayoutRegion} this
35340          */
35341         "expanded" : true,
35342         /**
35343          * @event slideshow
35344          * Fires when this region is slid into view.
35345          * @param {Roo.LayoutRegion} this
35346          */
35347         "slideshow" : true,
35348         /**
35349          * @event slidehide
35350          * Fires when this region slides out of view. 
35351          * @param {Roo.LayoutRegion} this
35352          */
35353         "slidehide" : true,
35354         /**
35355          * @event panelactivated
35356          * Fires when a panel is activated. 
35357          * @param {Roo.LayoutRegion} this
35358          * @param {Roo.ContentPanel} panel The activated panel
35359          */
35360         "panelactivated" : true,
35361         /**
35362          * @event resized
35363          * Fires when the user resizes this region. 
35364          * @param {Roo.LayoutRegion} this
35365          * @param {Number} newSize The new size (width for east/west, height for north/south)
35366          */
35367         "resized" : true
35368     };
35369     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35370     this.panels = new Roo.util.MixedCollection();
35371     this.panels.getKey = this.getPanelId.createDelegate(this);
35372     this.box = null;
35373     this.activePanel = null;
35374     // ensure listeners are added...
35375     
35376     if (config.listeners || config.events) {
35377         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35378             listeners : config.listeners || {},
35379             events : config.events || {}
35380         });
35381     }
35382     
35383     if(skipConfig !== true){
35384         this.applyConfig(config);
35385     }
35386 };
35387
35388 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35389 {
35390     getPanelId : function(p){
35391         return p.getId();
35392     },
35393     
35394     applyConfig : function(config){
35395         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35396         this.config = config;
35397         
35398     },
35399     
35400     /**
35401      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35402      * the width, for horizontal (north, south) the height.
35403      * @param {Number} newSize The new width or height
35404      */
35405     resizeTo : function(newSize){
35406         var el = this.el ? this.el :
35407                  (this.activePanel ? this.activePanel.getEl() : null);
35408         if(el){
35409             switch(this.position){
35410                 case "east":
35411                 case "west":
35412                     el.setWidth(newSize);
35413                     this.fireEvent("resized", this, newSize);
35414                 break;
35415                 case "north":
35416                 case "south":
35417                     el.setHeight(newSize);
35418                     this.fireEvent("resized", this, newSize);
35419                 break;                
35420             }
35421         }
35422     },
35423     
35424     getBox : function(){
35425         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35426     },
35427     
35428     getMargins : function(){
35429         return this.margins;
35430     },
35431     
35432     updateBox : function(box){
35433         this.box = box;
35434         var el = this.activePanel.getEl();
35435         el.dom.style.left = box.x + "px";
35436         el.dom.style.top = box.y + "px";
35437         this.activePanel.setSize(box.width, box.height);
35438     },
35439     
35440     /**
35441      * Returns the container element for this region.
35442      * @return {Roo.Element}
35443      */
35444     getEl : function(){
35445         return this.activePanel;
35446     },
35447     
35448     /**
35449      * Returns true if this region is currently visible.
35450      * @return {Boolean}
35451      */
35452     isVisible : function(){
35453         return this.activePanel ? true : false;
35454     },
35455     
35456     setActivePanel : function(panel){
35457         panel = this.getPanel(panel);
35458         if(this.activePanel && this.activePanel != panel){
35459             this.activePanel.setActiveState(false);
35460             this.activePanel.getEl().setLeftTop(-10000,-10000);
35461         }
35462         this.activePanel = panel;
35463         panel.setActiveState(true);
35464         if(this.box){
35465             panel.setSize(this.box.width, this.box.height);
35466         }
35467         this.fireEvent("panelactivated", this, panel);
35468         this.fireEvent("invalidated");
35469     },
35470     
35471     /**
35472      * Show the specified panel.
35473      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35474      * @return {Roo.ContentPanel} The shown panel or null
35475      */
35476     showPanel : function(panel){
35477         panel = this.getPanel(panel);
35478         if(panel){
35479             this.setActivePanel(panel);
35480         }
35481         return panel;
35482     },
35483     
35484     /**
35485      * Get the active panel for this region.
35486      * @return {Roo.ContentPanel} The active panel or null
35487      */
35488     getActivePanel : function(){
35489         return this.activePanel;
35490     },
35491     
35492     /**
35493      * Add the passed ContentPanel(s)
35494      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35495      * @return {Roo.ContentPanel} The panel added (if only one was added)
35496      */
35497     add : function(panel){
35498         if(arguments.length > 1){
35499             for(var i = 0, len = arguments.length; i < len; i++) {
35500                 this.add(arguments[i]);
35501             }
35502             return null;
35503         }
35504         if(this.hasPanel(panel)){
35505             this.showPanel(panel);
35506             return panel;
35507         }
35508         var el = panel.getEl();
35509         if(el.dom.parentNode != this.mgr.el.dom){
35510             this.mgr.el.dom.appendChild(el.dom);
35511         }
35512         if(panel.setRegion){
35513             panel.setRegion(this);
35514         }
35515         this.panels.add(panel);
35516         el.setStyle("position", "absolute");
35517         if(!panel.background){
35518             this.setActivePanel(panel);
35519             if(this.config.initialSize && this.panels.getCount()==1){
35520                 this.resizeTo(this.config.initialSize);
35521             }
35522         }
35523         this.fireEvent("paneladded", this, panel);
35524         return panel;
35525     },
35526     
35527     /**
35528      * Returns true if the panel is in this region.
35529      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35530      * @return {Boolean}
35531      */
35532     hasPanel : function(panel){
35533         if(typeof panel == "object"){ // must be panel obj
35534             panel = panel.getId();
35535         }
35536         return this.getPanel(panel) ? true : false;
35537     },
35538     
35539     /**
35540      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35541      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35542      * @param {Boolean} preservePanel Overrides the config preservePanel option
35543      * @return {Roo.ContentPanel} The panel that was removed
35544      */
35545     remove : function(panel, preservePanel){
35546         panel = this.getPanel(panel);
35547         if(!panel){
35548             return null;
35549         }
35550         var e = {};
35551         this.fireEvent("beforeremove", this, panel, e);
35552         if(e.cancel === true){
35553             return null;
35554         }
35555         var panelId = panel.getId();
35556         this.panels.removeKey(panelId);
35557         return panel;
35558     },
35559     
35560     /**
35561      * Returns the panel specified or null if it's not in this region.
35562      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35563      * @return {Roo.ContentPanel}
35564      */
35565     getPanel : function(id){
35566         if(typeof id == "object"){ // must be panel obj
35567             return id;
35568         }
35569         return this.panels.get(id);
35570     },
35571     
35572     /**
35573      * Returns this regions position (north/south/east/west/center).
35574      * @return {String} 
35575      */
35576     getPosition: function(){
35577         return this.position;    
35578     }
35579 });/*
35580  * Based on:
35581  * Ext JS Library 1.1.1
35582  * Copyright(c) 2006-2007, Ext JS, LLC.
35583  *
35584  * Originally Released Under LGPL - original licence link has changed is not relivant.
35585  *
35586  * Fork - LGPL
35587  * <script type="text/javascript">
35588  */
35589  
35590 /**
35591  * @class Roo.bootstrap.layout.Region
35592  * @extends Roo.bootstrap.layout.Basic
35593  * This class represents a region in a layout manager.
35594  
35595  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35596  * @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})
35597  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35598  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35599  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35600  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35601  * @cfg {String}    title           The title for the region (overrides panel titles)
35602  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35603  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35604  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35605  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35606  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35607  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35608  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35609  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35610  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35611  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35612
35613  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35614  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35615  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35616  * @cfg {Number}    width           For East/West panels
35617  * @cfg {Number}    height          For North/South panels
35618  * @cfg {Boolean}   split           To show the splitter
35619  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35620  * 
35621  * @cfg {string}   cls             Extra CSS classes to add to region
35622  * 
35623  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35624  * @cfg {string}   region  the region that it inhabits..
35625  *
35626
35627  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35628  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35629
35630  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35631  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35632  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35633  */
35634 Roo.bootstrap.layout.Region = function(config)
35635 {
35636     this.applyConfig(config);
35637
35638     var mgr = config.mgr;
35639     var pos = config.region;
35640     config.skipConfig = true;
35641     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35642     
35643     if (mgr.el) {
35644         this.onRender(mgr.el);   
35645     }
35646      
35647     this.visible = true;
35648     this.collapsed = false;
35649     this.unrendered_panels = [];
35650 };
35651
35652 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35653
35654     position: '', // set by wrapper (eg. north/south etc..)
35655     unrendered_panels : null,  // unrendered panels.
35656     createBody : function(){
35657         /** This region's body element 
35658         * @type Roo.Element */
35659         this.bodyEl = this.el.createChild({
35660                 tag: "div",
35661                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35662         });
35663     },
35664
35665     onRender: function(ctr, pos)
35666     {
35667         var dh = Roo.DomHelper;
35668         /** This region's container element 
35669         * @type Roo.Element */
35670         this.el = dh.append(ctr.dom, {
35671                 tag: "div",
35672                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35673             }, true);
35674         /** This region's title element 
35675         * @type Roo.Element */
35676     
35677         this.titleEl = dh.append(this.el.dom,
35678             {
35679                     tag: "div",
35680                     unselectable: "on",
35681                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35682                     children:[
35683                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35684                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35685                     ]}, true);
35686         
35687         this.titleEl.enableDisplayMode();
35688         /** This region's title text element 
35689         * @type HTMLElement */
35690         this.titleTextEl = this.titleEl.dom.firstChild;
35691         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35692         /*
35693         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35694         this.closeBtn.enableDisplayMode();
35695         this.closeBtn.on("click", this.closeClicked, this);
35696         this.closeBtn.hide();
35697     */
35698         this.createBody(this.config);
35699         if(this.config.hideWhenEmpty){
35700             this.hide();
35701             this.on("paneladded", this.validateVisibility, this);
35702             this.on("panelremoved", this.validateVisibility, this);
35703         }
35704         if(this.autoScroll){
35705             this.bodyEl.setStyle("overflow", "auto");
35706         }else{
35707             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35708         }
35709         //if(c.titlebar !== false){
35710             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35711                 this.titleEl.hide();
35712             }else{
35713                 this.titleEl.show();
35714                 if(this.config.title){
35715                     this.titleTextEl.innerHTML = this.config.title;
35716                 }
35717             }
35718         //}
35719         if(this.config.collapsed){
35720             this.collapse(true);
35721         }
35722         if(this.config.hidden){
35723             this.hide();
35724         }
35725         
35726         if (this.unrendered_panels && this.unrendered_panels.length) {
35727             for (var i =0;i< this.unrendered_panels.length; i++) {
35728                 this.add(this.unrendered_panels[i]);
35729             }
35730             this.unrendered_panels = null;
35731             
35732         }
35733         
35734     },
35735     
35736     applyConfig : function(c)
35737     {
35738         /*
35739          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35740             var dh = Roo.DomHelper;
35741             if(c.titlebar !== false){
35742                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35743                 this.collapseBtn.on("click", this.collapse, this);
35744                 this.collapseBtn.enableDisplayMode();
35745                 /*
35746                 if(c.showPin === true || this.showPin){
35747                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35748                     this.stickBtn.enableDisplayMode();
35749                     this.stickBtn.on("click", this.expand, this);
35750                     this.stickBtn.hide();
35751                 }
35752                 
35753             }
35754             */
35755             /** This region's collapsed element
35756             * @type Roo.Element */
35757             /*
35758              *
35759             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35760                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35761             ]}, true);
35762             
35763             if(c.floatable !== false){
35764                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35765                this.collapsedEl.on("click", this.collapseClick, this);
35766             }
35767
35768             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35769                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35770                    id: "message", unselectable: "on", style:{"float":"left"}});
35771                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35772              }
35773             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35774             this.expandBtn.on("click", this.expand, this);
35775             
35776         }
35777         
35778         if(this.collapseBtn){
35779             this.collapseBtn.setVisible(c.collapsible == true);
35780         }
35781         
35782         this.cmargins = c.cmargins || this.cmargins ||
35783                          (this.position == "west" || this.position == "east" ?
35784                              {top: 0, left: 2, right:2, bottom: 0} :
35785                              {top: 2, left: 0, right:0, bottom: 2});
35786         */
35787         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35788         
35789         
35790         this.bottomTabs = c.tabPosition != "top";
35791         
35792         this.autoScroll = c.autoScroll || false;
35793         
35794         
35795        
35796         
35797         this.duration = c.duration || .30;
35798         this.slideDuration = c.slideDuration || .45;
35799         this.config = c;
35800        
35801     },
35802     /**
35803      * Returns true if this region is currently visible.
35804      * @return {Boolean}
35805      */
35806     isVisible : function(){
35807         return this.visible;
35808     },
35809
35810     /**
35811      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35812      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35813      */
35814     //setCollapsedTitle : function(title){
35815     //    title = title || "&#160;";
35816      //   if(this.collapsedTitleTextEl){
35817       //      this.collapsedTitleTextEl.innerHTML = title;
35818        // }
35819     //},
35820
35821     getBox : function(){
35822         var b;
35823       //  if(!this.collapsed){
35824             b = this.el.getBox(false, true);
35825        // }else{
35826           //  b = this.collapsedEl.getBox(false, true);
35827         //}
35828         return b;
35829     },
35830
35831     getMargins : function(){
35832         return this.margins;
35833         //return this.collapsed ? this.cmargins : this.margins;
35834     },
35835 /*
35836     highlight : function(){
35837         this.el.addClass("x-layout-panel-dragover");
35838     },
35839
35840     unhighlight : function(){
35841         this.el.removeClass("x-layout-panel-dragover");
35842     },
35843 */
35844     updateBox : function(box)
35845     {
35846         if (!this.bodyEl) {
35847             return; // not rendered yet..
35848         }
35849         
35850         this.box = box;
35851         if(!this.collapsed){
35852             this.el.dom.style.left = box.x + "px";
35853             this.el.dom.style.top = box.y + "px";
35854             this.updateBody(box.width, box.height);
35855         }else{
35856             this.collapsedEl.dom.style.left = box.x + "px";
35857             this.collapsedEl.dom.style.top = box.y + "px";
35858             this.collapsedEl.setSize(box.width, box.height);
35859         }
35860         if(this.tabs){
35861             this.tabs.autoSizeTabs();
35862         }
35863     },
35864
35865     updateBody : function(w, h)
35866     {
35867         if(w !== null){
35868             this.el.setWidth(w);
35869             w -= this.el.getBorderWidth("rl");
35870             if(this.config.adjustments){
35871                 w += this.config.adjustments[0];
35872             }
35873         }
35874         if(h !== null && h > 0){
35875             this.el.setHeight(h);
35876             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35877             h -= this.el.getBorderWidth("tb");
35878             if(this.config.adjustments){
35879                 h += this.config.adjustments[1];
35880             }
35881             this.bodyEl.setHeight(h);
35882             if(this.tabs){
35883                 h = this.tabs.syncHeight(h);
35884             }
35885         }
35886         if(this.panelSize){
35887             w = w !== null ? w : this.panelSize.width;
35888             h = h !== null ? h : this.panelSize.height;
35889         }
35890         if(this.activePanel){
35891             var el = this.activePanel.getEl();
35892             w = w !== null ? w : el.getWidth();
35893             h = h !== null ? h : el.getHeight();
35894             this.panelSize = {width: w, height: h};
35895             this.activePanel.setSize(w, h);
35896         }
35897         if(Roo.isIE && this.tabs){
35898             this.tabs.el.repaint();
35899         }
35900     },
35901
35902     /**
35903      * Returns the container element for this region.
35904      * @return {Roo.Element}
35905      */
35906     getEl : function(){
35907         return this.el;
35908     },
35909
35910     /**
35911      * Hides this region.
35912      */
35913     hide : function(){
35914         //if(!this.collapsed){
35915             this.el.dom.style.left = "-2000px";
35916             this.el.hide();
35917         //}else{
35918          //   this.collapsedEl.dom.style.left = "-2000px";
35919          //   this.collapsedEl.hide();
35920        // }
35921         this.visible = false;
35922         this.fireEvent("visibilitychange", this, false);
35923     },
35924
35925     /**
35926      * Shows this region if it was previously hidden.
35927      */
35928     show : function(){
35929         //if(!this.collapsed){
35930             this.el.show();
35931         //}else{
35932         //    this.collapsedEl.show();
35933        // }
35934         this.visible = true;
35935         this.fireEvent("visibilitychange", this, true);
35936     },
35937 /*
35938     closeClicked : function(){
35939         if(this.activePanel){
35940             this.remove(this.activePanel);
35941         }
35942     },
35943
35944     collapseClick : function(e){
35945         if(this.isSlid){
35946            e.stopPropagation();
35947            this.slideIn();
35948         }else{
35949            e.stopPropagation();
35950            this.slideOut();
35951         }
35952     },
35953 */
35954     /**
35955      * Collapses this region.
35956      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35957      */
35958     /*
35959     collapse : function(skipAnim, skipCheck = false){
35960         if(this.collapsed) {
35961             return;
35962         }
35963         
35964         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35965             
35966             this.collapsed = true;
35967             if(this.split){
35968                 this.split.el.hide();
35969             }
35970             if(this.config.animate && skipAnim !== true){
35971                 this.fireEvent("invalidated", this);
35972                 this.animateCollapse();
35973             }else{
35974                 this.el.setLocation(-20000,-20000);
35975                 this.el.hide();
35976                 this.collapsedEl.show();
35977                 this.fireEvent("collapsed", this);
35978                 this.fireEvent("invalidated", this);
35979             }
35980         }
35981         
35982     },
35983 */
35984     animateCollapse : function(){
35985         // overridden
35986     },
35987
35988     /**
35989      * Expands this region if it was previously collapsed.
35990      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35991      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35992      */
35993     /*
35994     expand : function(e, skipAnim){
35995         if(e) {
35996             e.stopPropagation();
35997         }
35998         if(!this.collapsed || this.el.hasActiveFx()) {
35999             return;
36000         }
36001         if(this.isSlid){
36002             this.afterSlideIn();
36003             skipAnim = true;
36004         }
36005         this.collapsed = false;
36006         if(this.config.animate && skipAnim !== true){
36007             this.animateExpand();
36008         }else{
36009             this.el.show();
36010             if(this.split){
36011                 this.split.el.show();
36012             }
36013             this.collapsedEl.setLocation(-2000,-2000);
36014             this.collapsedEl.hide();
36015             this.fireEvent("invalidated", this);
36016             this.fireEvent("expanded", this);
36017         }
36018     },
36019 */
36020     animateExpand : function(){
36021         // overridden
36022     },
36023
36024     initTabs : function()
36025     {
36026         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36027         
36028         var ts = new Roo.bootstrap.panel.Tabs({
36029                 el: this.bodyEl.dom,
36030                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36031                 disableTooltips: this.config.disableTabTips,
36032                 toolbar : this.config.toolbar
36033             });
36034         
36035         if(this.config.hideTabs){
36036             ts.stripWrap.setDisplayed(false);
36037         }
36038         this.tabs = ts;
36039         ts.resizeTabs = this.config.resizeTabs === true;
36040         ts.minTabWidth = this.config.minTabWidth || 40;
36041         ts.maxTabWidth = this.config.maxTabWidth || 250;
36042         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36043         ts.monitorResize = false;
36044         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36045         ts.bodyEl.addClass('roo-layout-tabs-body');
36046         this.panels.each(this.initPanelAsTab, this);
36047     },
36048
36049     initPanelAsTab : function(panel){
36050         var ti = this.tabs.addTab(
36051             panel.getEl().id,
36052             panel.getTitle(),
36053             null,
36054             this.config.closeOnTab && panel.isClosable(),
36055             panel.tpl
36056         );
36057         if(panel.tabTip !== undefined){
36058             ti.setTooltip(panel.tabTip);
36059         }
36060         ti.on("activate", function(){
36061               this.setActivePanel(panel);
36062         }, this);
36063         
36064         if(this.config.closeOnTab){
36065             ti.on("beforeclose", function(t, e){
36066                 e.cancel = true;
36067                 this.remove(panel);
36068             }, this);
36069         }
36070         
36071         panel.tabItem = ti;
36072         
36073         return ti;
36074     },
36075
36076     updatePanelTitle : function(panel, title)
36077     {
36078         if(this.activePanel == panel){
36079             this.updateTitle(title);
36080         }
36081         if(this.tabs){
36082             var ti = this.tabs.getTab(panel.getEl().id);
36083             ti.setText(title);
36084             if(panel.tabTip !== undefined){
36085                 ti.setTooltip(panel.tabTip);
36086             }
36087         }
36088     },
36089
36090     updateTitle : function(title){
36091         if(this.titleTextEl && !this.config.title){
36092             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36093         }
36094     },
36095
36096     setActivePanel : function(panel)
36097     {
36098         panel = this.getPanel(panel);
36099         if(this.activePanel && this.activePanel != panel){
36100             if(this.activePanel.setActiveState(false) === false){
36101                 return;
36102             }
36103         }
36104         this.activePanel = panel;
36105         panel.setActiveState(true);
36106         if(this.panelSize){
36107             panel.setSize(this.panelSize.width, this.panelSize.height);
36108         }
36109         if(this.closeBtn){
36110             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36111         }
36112         this.updateTitle(panel.getTitle());
36113         if(this.tabs){
36114             this.fireEvent("invalidated", this);
36115         }
36116         this.fireEvent("panelactivated", this, panel);
36117     },
36118
36119     /**
36120      * Shows the specified panel.
36121      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36122      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36123      */
36124     showPanel : function(panel)
36125     {
36126         panel = this.getPanel(panel);
36127         if(panel){
36128             if(this.tabs){
36129                 var tab = this.tabs.getTab(panel.getEl().id);
36130                 if(tab.isHidden()){
36131                     this.tabs.unhideTab(tab.id);
36132                 }
36133                 tab.activate();
36134             }else{
36135                 this.setActivePanel(panel);
36136             }
36137         }
36138         return panel;
36139     },
36140
36141     /**
36142      * Get the active panel for this region.
36143      * @return {Roo.ContentPanel} The active panel or null
36144      */
36145     getActivePanel : function(){
36146         return this.activePanel;
36147     },
36148
36149     validateVisibility : function(){
36150         if(this.panels.getCount() < 1){
36151             this.updateTitle("&#160;");
36152             this.closeBtn.hide();
36153             this.hide();
36154         }else{
36155             if(!this.isVisible()){
36156                 this.show();
36157             }
36158         }
36159     },
36160
36161     /**
36162      * Adds the passed ContentPanel(s) to this region.
36163      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36164      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36165      */
36166     add : function(panel)
36167     {
36168         if(arguments.length > 1){
36169             for(var i = 0, len = arguments.length; i < len; i++) {
36170                 this.add(arguments[i]);
36171             }
36172             return null;
36173         }
36174         
36175         // if we have not been rendered yet, then we can not really do much of this..
36176         if (!this.bodyEl) {
36177             this.unrendered_panels.push(panel);
36178             return panel;
36179         }
36180         
36181         
36182         
36183         
36184         if(this.hasPanel(panel)){
36185             this.showPanel(panel);
36186             return panel;
36187         }
36188         panel.setRegion(this);
36189         this.panels.add(panel);
36190        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36191             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36192             // and hide them... ???
36193             this.bodyEl.dom.appendChild(panel.getEl().dom);
36194             if(panel.background !== true){
36195                 this.setActivePanel(panel);
36196             }
36197             this.fireEvent("paneladded", this, panel);
36198             return panel;
36199         }
36200         */
36201         if(!this.tabs){
36202             this.initTabs();
36203         }else{
36204             this.initPanelAsTab(panel);
36205         }
36206         
36207         
36208         if(panel.background !== true){
36209             this.tabs.activate(panel.getEl().id);
36210         }
36211         this.fireEvent("paneladded", this, panel);
36212         return panel;
36213     },
36214
36215     /**
36216      * Hides the tab for the specified panel.
36217      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36218      */
36219     hidePanel : function(panel){
36220         if(this.tabs && (panel = this.getPanel(panel))){
36221             this.tabs.hideTab(panel.getEl().id);
36222         }
36223     },
36224
36225     /**
36226      * Unhides the tab for a previously hidden panel.
36227      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36228      */
36229     unhidePanel : function(panel){
36230         if(this.tabs && (panel = this.getPanel(panel))){
36231             this.tabs.unhideTab(panel.getEl().id);
36232         }
36233     },
36234
36235     clearPanels : function(){
36236         while(this.panels.getCount() > 0){
36237              this.remove(this.panels.first());
36238         }
36239     },
36240
36241     /**
36242      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36243      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36244      * @param {Boolean} preservePanel Overrides the config preservePanel option
36245      * @return {Roo.ContentPanel} The panel that was removed
36246      */
36247     remove : function(panel, preservePanel)
36248     {
36249         panel = this.getPanel(panel);
36250         if(!panel){
36251             return null;
36252         }
36253         var e = {};
36254         this.fireEvent("beforeremove", this, panel, e);
36255         if(e.cancel === true){
36256             return null;
36257         }
36258         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36259         var panelId = panel.getId();
36260         this.panels.removeKey(panelId);
36261         if(preservePanel){
36262             document.body.appendChild(panel.getEl().dom);
36263         }
36264         if(this.tabs){
36265             this.tabs.removeTab(panel.getEl().id);
36266         }else if (!preservePanel){
36267             this.bodyEl.dom.removeChild(panel.getEl().dom);
36268         }
36269         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36270             var p = this.panels.first();
36271             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36272             tempEl.appendChild(p.getEl().dom);
36273             this.bodyEl.update("");
36274             this.bodyEl.dom.appendChild(p.getEl().dom);
36275             tempEl = null;
36276             this.updateTitle(p.getTitle());
36277             this.tabs = null;
36278             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36279             this.setActivePanel(p);
36280         }
36281         panel.setRegion(null);
36282         if(this.activePanel == panel){
36283             this.activePanel = null;
36284         }
36285         if(this.config.autoDestroy !== false && preservePanel !== true){
36286             try{panel.destroy();}catch(e){}
36287         }
36288         this.fireEvent("panelremoved", this, panel);
36289         return panel;
36290     },
36291
36292     /**
36293      * Returns the TabPanel component used by this region
36294      * @return {Roo.TabPanel}
36295      */
36296     getTabs : function(){
36297         return this.tabs;
36298     },
36299
36300     createTool : function(parentEl, className){
36301         var btn = Roo.DomHelper.append(parentEl, {
36302             tag: "div",
36303             cls: "x-layout-tools-button",
36304             children: [ {
36305                 tag: "div",
36306                 cls: "roo-layout-tools-button-inner " + className,
36307                 html: "&#160;"
36308             }]
36309         }, true);
36310         btn.addClassOnOver("roo-layout-tools-button-over");
36311         return btn;
36312     }
36313 });/*
36314  * Based on:
36315  * Ext JS Library 1.1.1
36316  * Copyright(c) 2006-2007, Ext JS, LLC.
36317  *
36318  * Originally Released Under LGPL - original licence link has changed is not relivant.
36319  *
36320  * Fork - LGPL
36321  * <script type="text/javascript">
36322  */
36323  
36324
36325
36326 /**
36327  * @class Roo.SplitLayoutRegion
36328  * @extends Roo.LayoutRegion
36329  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36330  */
36331 Roo.bootstrap.layout.Split = function(config){
36332     this.cursor = config.cursor;
36333     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36334 };
36335
36336 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36337 {
36338     splitTip : "Drag to resize.",
36339     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36340     useSplitTips : false,
36341
36342     applyConfig : function(config){
36343         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36344     },
36345     
36346     onRender : function(ctr,pos) {
36347         
36348         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36349         if(!this.config.split){
36350             return;
36351         }
36352         if(!this.split){
36353             
36354             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36355                             tag: "div",
36356                             id: this.el.id + "-split",
36357                             cls: "roo-layout-split roo-layout-split-"+this.position,
36358                             html: "&#160;"
36359             });
36360             /** The SplitBar for this region 
36361             * @type Roo.SplitBar */
36362             // does not exist yet...
36363             Roo.log([this.position, this.orientation]);
36364             
36365             this.split = new Roo.bootstrap.SplitBar({
36366                 dragElement : splitEl,
36367                 resizingElement: this.el,
36368                 orientation : this.orientation
36369             });
36370             
36371             this.split.on("moved", this.onSplitMove, this);
36372             this.split.useShim = this.config.useShim === true;
36373             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36374             if(this.useSplitTips){
36375                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36376             }
36377             //if(config.collapsible){
36378             //    this.split.el.on("dblclick", this.collapse,  this);
36379             //}
36380         }
36381         if(typeof this.config.minSize != "undefined"){
36382             this.split.minSize = this.config.minSize;
36383         }
36384         if(typeof this.config.maxSize != "undefined"){
36385             this.split.maxSize = this.config.maxSize;
36386         }
36387         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36388             this.hideSplitter();
36389         }
36390         
36391     },
36392
36393     getHMaxSize : function(){
36394          var cmax = this.config.maxSize || 10000;
36395          var center = this.mgr.getRegion("center");
36396          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36397     },
36398
36399     getVMaxSize : function(){
36400          var cmax = this.config.maxSize || 10000;
36401          var center = this.mgr.getRegion("center");
36402          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36403     },
36404
36405     onSplitMove : function(split, newSize){
36406         this.fireEvent("resized", this, newSize);
36407     },
36408     
36409     /** 
36410      * Returns the {@link Roo.SplitBar} for this region.
36411      * @return {Roo.SplitBar}
36412      */
36413     getSplitBar : function(){
36414         return this.split;
36415     },
36416     
36417     hide : function(){
36418         this.hideSplitter();
36419         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36420     },
36421
36422     hideSplitter : function(){
36423         if(this.split){
36424             this.split.el.setLocation(-2000,-2000);
36425             this.split.el.hide();
36426         }
36427     },
36428
36429     show : function(){
36430         if(this.split){
36431             this.split.el.show();
36432         }
36433         Roo.bootstrap.layout.Split.superclass.show.call(this);
36434     },
36435     
36436     beforeSlide: function(){
36437         if(Roo.isGecko){// firefox overflow auto bug workaround
36438             this.bodyEl.clip();
36439             if(this.tabs) {
36440                 this.tabs.bodyEl.clip();
36441             }
36442             if(this.activePanel){
36443                 this.activePanel.getEl().clip();
36444                 
36445                 if(this.activePanel.beforeSlide){
36446                     this.activePanel.beforeSlide();
36447                 }
36448             }
36449         }
36450     },
36451     
36452     afterSlide : function(){
36453         if(Roo.isGecko){// firefox overflow auto bug workaround
36454             this.bodyEl.unclip();
36455             if(this.tabs) {
36456                 this.tabs.bodyEl.unclip();
36457             }
36458             if(this.activePanel){
36459                 this.activePanel.getEl().unclip();
36460                 if(this.activePanel.afterSlide){
36461                     this.activePanel.afterSlide();
36462                 }
36463             }
36464         }
36465     },
36466
36467     initAutoHide : function(){
36468         if(this.autoHide !== false){
36469             if(!this.autoHideHd){
36470                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36471                 this.autoHideHd = {
36472                     "mouseout": function(e){
36473                         if(!e.within(this.el, true)){
36474                             st.delay(500);
36475                         }
36476                     },
36477                     "mouseover" : function(e){
36478                         st.cancel();
36479                     },
36480                     scope : this
36481                 };
36482             }
36483             this.el.on(this.autoHideHd);
36484         }
36485     },
36486
36487     clearAutoHide : function(){
36488         if(this.autoHide !== false){
36489             this.el.un("mouseout", this.autoHideHd.mouseout);
36490             this.el.un("mouseover", this.autoHideHd.mouseover);
36491         }
36492     },
36493
36494     clearMonitor : function(){
36495         Roo.get(document).un("click", this.slideInIf, this);
36496     },
36497
36498     // these names are backwards but not changed for compat
36499     slideOut : function(){
36500         if(this.isSlid || this.el.hasActiveFx()){
36501             return;
36502         }
36503         this.isSlid = true;
36504         if(this.collapseBtn){
36505             this.collapseBtn.hide();
36506         }
36507         this.closeBtnState = this.closeBtn.getStyle('display');
36508         this.closeBtn.hide();
36509         if(this.stickBtn){
36510             this.stickBtn.show();
36511         }
36512         this.el.show();
36513         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36514         this.beforeSlide();
36515         this.el.setStyle("z-index", 10001);
36516         this.el.slideIn(this.getSlideAnchor(), {
36517             callback: function(){
36518                 this.afterSlide();
36519                 this.initAutoHide();
36520                 Roo.get(document).on("click", this.slideInIf, this);
36521                 this.fireEvent("slideshow", this);
36522             },
36523             scope: this,
36524             block: true
36525         });
36526     },
36527
36528     afterSlideIn : function(){
36529         this.clearAutoHide();
36530         this.isSlid = false;
36531         this.clearMonitor();
36532         this.el.setStyle("z-index", "");
36533         if(this.collapseBtn){
36534             this.collapseBtn.show();
36535         }
36536         this.closeBtn.setStyle('display', this.closeBtnState);
36537         if(this.stickBtn){
36538             this.stickBtn.hide();
36539         }
36540         this.fireEvent("slidehide", this);
36541     },
36542
36543     slideIn : function(cb){
36544         if(!this.isSlid || this.el.hasActiveFx()){
36545             Roo.callback(cb);
36546             return;
36547         }
36548         this.isSlid = false;
36549         this.beforeSlide();
36550         this.el.slideOut(this.getSlideAnchor(), {
36551             callback: function(){
36552                 this.el.setLeftTop(-10000, -10000);
36553                 this.afterSlide();
36554                 this.afterSlideIn();
36555                 Roo.callback(cb);
36556             },
36557             scope: this,
36558             block: true
36559         });
36560     },
36561     
36562     slideInIf : function(e){
36563         if(!e.within(this.el)){
36564             this.slideIn();
36565         }
36566     },
36567
36568     animateCollapse : function(){
36569         this.beforeSlide();
36570         this.el.setStyle("z-index", 20000);
36571         var anchor = this.getSlideAnchor();
36572         this.el.slideOut(anchor, {
36573             callback : function(){
36574                 this.el.setStyle("z-index", "");
36575                 this.collapsedEl.slideIn(anchor, {duration:.3});
36576                 this.afterSlide();
36577                 this.el.setLocation(-10000,-10000);
36578                 this.el.hide();
36579                 this.fireEvent("collapsed", this);
36580             },
36581             scope: this,
36582             block: true
36583         });
36584     },
36585
36586     animateExpand : function(){
36587         this.beforeSlide();
36588         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36589         this.el.setStyle("z-index", 20000);
36590         this.collapsedEl.hide({
36591             duration:.1
36592         });
36593         this.el.slideIn(this.getSlideAnchor(), {
36594             callback : function(){
36595                 this.el.setStyle("z-index", "");
36596                 this.afterSlide();
36597                 if(this.split){
36598                     this.split.el.show();
36599                 }
36600                 this.fireEvent("invalidated", this);
36601                 this.fireEvent("expanded", this);
36602             },
36603             scope: this,
36604             block: true
36605         });
36606     },
36607
36608     anchors : {
36609         "west" : "left",
36610         "east" : "right",
36611         "north" : "top",
36612         "south" : "bottom"
36613     },
36614
36615     sanchors : {
36616         "west" : "l",
36617         "east" : "r",
36618         "north" : "t",
36619         "south" : "b"
36620     },
36621
36622     canchors : {
36623         "west" : "tl-tr",
36624         "east" : "tr-tl",
36625         "north" : "tl-bl",
36626         "south" : "bl-tl"
36627     },
36628
36629     getAnchor : function(){
36630         return this.anchors[this.position];
36631     },
36632
36633     getCollapseAnchor : function(){
36634         return this.canchors[this.position];
36635     },
36636
36637     getSlideAnchor : function(){
36638         return this.sanchors[this.position];
36639     },
36640
36641     getAlignAdj : function(){
36642         var cm = this.cmargins;
36643         switch(this.position){
36644             case "west":
36645                 return [0, 0];
36646             break;
36647             case "east":
36648                 return [0, 0];
36649             break;
36650             case "north":
36651                 return [0, 0];
36652             break;
36653             case "south":
36654                 return [0, 0];
36655             break;
36656         }
36657     },
36658
36659     getExpandAdj : function(){
36660         var c = this.collapsedEl, cm = this.cmargins;
36661         switch(this.position){
36662             case "west":
36663                 return [-(cm.right+c.getWidth()+cm.left), 0];
36664             break;
36665             case "east":
36666                 return [cm.right+c.getWidth()+cm.left, 0];
36667             break;
36668             case "north":
36669                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36670             break;
36671             case "south":
36672                 return [0, cm.top+cm.bottom+c.getHeight()];
36673             break;
36674         }
36675     }
36676 });/*
36677  * Based on:
36678  * Ext JS Library 1.1.1
36679  * Copyright(c) 2006-2007, Ext JS, LLC.
36680  *
36681  * Originally Released Under LGPL - original licence link has changed is not relivant.
36682  *
36683  * Fork - LGPL
36684  * <script type="text/javascript">
36685  */
36686 /*
36687  * These classes are private internal classes
36688  */
36689 Roo.bootstrap.layout.Center = function(config){
36690     config.region = "center";
36691     Roo.bootstrap.layout.Region.call(this, config);
36692     this.visible = true;
36693     this.minWidth = config.minWidth || 20;
36694     this.minHeight = config.minHeight || 20;
36695 };
36696
36697 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36698     hide : function(){
36699         // center panel can't be hidden
36700     },
36701     
36702     show : function(){
36703         // center panel can't be hidden
36704     },
36705     
36706     getMinWidth: function(){
36707         return this.minWidth;
36708     },
36709     
36710     getMinHeight: function(){
36711         return this.minHeight;
36712     }
36713 });
36714
36715
36716
36717
36718  
36719
36720
36721
36722
36723
36724 Roo.bootstrap.layout.North = function(config)
36725 {
36726     config.region = 'north';
36727     config.cursor = 'n-resize';
36728     
36729     Roo.bootstrap.layout.Split.call(this, config);
36730     
36731     
36732     if(this.split){
36733         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36734         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36735         this.split.el.addClass("roo-layout-split-v");
36736     }
36737     var size = config.initialSize || config.height;
36738     if(typeof size != "undefined"){
36739         this.el.setHeight(size);
36740     }
36741 };
36742 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36743 {
36744     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36745     
36746     
36747     
36748     getBox : function(){
36749         if(this.collapsed){
36750             return this.collapsedEl.getBox();
36751         }
36752         var box = this.el.getBox();
36753         if(this.split){
36754             box.height += this.split.el.getHeight();
36755         }
36756         return box;
36757     },
36758     
36759     updateBox : function(box){
36760         if(this.split && !this.collapsed){
36761             box.height -= this.split.el.getHeight();
36762             this.split.el.setLeft(box.x);
36763             this.split.el.setTop(box.y+box.height);
36764             this.split.el.setWidth(box.width);
36765         }
36766         if(this.collapsed){
36767             this.updateBody(box.width, null);
36768         }
36769         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36770     }
36771 });
36772
36773
36774
36775
36776
36777 Roo.bootstrap.layout.South = function(config){
36778     config.region = 'south';
36779     config.cursor = 's-resize';
36780     Roo.bootstrap.layout.Split.call(this, config);
36781     if(this.split){
36782         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36783         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36784         this.split.el.addClass("roo-layout-split-v");
36785     }
36786     var size = config.initialSize || config.height;
36787     if(typeof size != "undefined"){
36788         this.el.setHeight(size);
36789     }
36790 };
36791
36792 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36793     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36794     getBox : function(){
36795         if(this.collapsed){
36796             return this.collapsedEl.getBox();
36797         }
36798         var box = this.el.getBox();
36799         if(this.split){
36800             var sh = this.split.el.getHeight();
36801             box.height += sh;
36802             box.y -= sh;
36803         }
36804         return box;
36805     },
36806     
36807     updateBox : function(box){
36808         if(this.split && !this.collapsed){
36809             var sh = this.split.el.getHeight();
36810             box.height -= sh;
36811             box.y += sh;
36812             this.split.el.setLeft(box.x);
36813             this.split.el.setTop(box.y-sh);
36814             this.split.el.setWidth(box.width);
36815         }
36816         if(this.collapsed){
36817             this.updateBody(box.width, null);
36818         }
36819         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36820     }
36821 });
36822
36823 Roo.bootstrap.layout.East = function(config){
36824     config.region = "east";
36825     config.cursor = "e-resize";
36826     Roo.bootstrap.layout.Split.call(this, config);
36827     if(this.split){
36828         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36829         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36830         this.split.el.addClass("roo-layout-split-h");
36831     }
36832     var size = config.initialSize || config.width;
36833     if(typeof size != "undefined"){
36834         this.el.setWidth(size);
36835     }
36836 };
36837 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36838     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36839     getBox : function(){
36840         if(this.collapsed){
36841             return this.collapsedEl.getBox();
36842         }
36843         var box = this.el.getBox();
36844         if(this.split){
36845             var sw = this.split.el.getWidth();
36846             box.width += sw;
36847             box.x -= sw;
36848         }
36849         return box;
36850     },
36851
36852     updateBox : function(box){
36853         if(this.split && !this.collapsed){
36854             var sw = this.split.el.getWidth();
36855             box.width -= sw;
36856             this.split.el.setLeft(box.x);
36857             this.split.el.setTop(box.y);
36858             this.split.el.setHeight(box.height);
36859             box.x += sw;
36860         }
36861         if(this.collapsed){
36862             this.updateBody(null, box.height);
36863         }
36864         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36865     }
36866 });
36867
36868 Roo.bootstrap.layout.West = function(config){
36869     config.region = "west";
36870     config.cursor = "w-resize";
36871     
36872     Roo.bootstrap.layout.Split.call(this, config);
36873     if(this.split){
36874         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36875         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36876         this.split.el.addClass("roo-layout-split-h");
36877     }
36878     
36879 };
36880 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36881     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36882     
36883     onRender: function(ctr, pos)
36884     {
36885         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36886         var size = this.config.initialSize || this.config.width;
36887         if(typeof size != "undefined"){
36888             this.el.setWidth(size);
36889         }
36890     },
36891     
36892     getBox : function(){
36893         if(this.collapsed){
36894             return this.collapsedEl.getBox();
36895         }
36896         var box = this.el.getBox();
36897         if(this.split){
36898             box.width += this.split.el.getWidth();
36899         }
36900         return box;
36901     },
36902     
36903     updateBox : function(box){
36904         if(this.split && !this.collapsed){
36905             var sw = this.split.el.getWidth();
36906             box.width -= sw;
36907             this.split.el.setLeft(box.x+box.width);
36908             this.split.el.setTop(box.y);
36909             this.split.el.setHeight(box.height);
36910         }
36911         if(this.collapsed){
36912             this.updateBody(null, box.height);
36913         }
36914         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36915     }
36916 });
36917 Roo.namespace("Roo.bootstrap.panel");/*
36918  * Based on:
36919  * Ext JS Library 1.1.1
36920  * Copyright(c) 2006-2007, Ext JS, LLC.
36921  *
36922  * Originally Released Under LGPL - original licence link has changed is not relivant.
36923  *
36924  * Fork - LGPL
36925  * <script type="text/javascript">
36926  */
36927 /**
36928  * @class Roo.ContentPanel
36929  * @extends Roo.util.Observable
36930  * A basic ContentPanel element.
36931  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36932  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36933  * @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
36934  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36935  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36936  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36937  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36938  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36939  * @cfg {String} title          The title for this panel
36940  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36941  * @cfg {String} url            Calls {@link #setUrl} with this value
36942  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36943  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36944  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36945  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36946  * @cfg {Boolean} badges render the badges
36947
36948  * @constructor
36949  * Create a new ContentPanel.
36950  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36951  * @param {String/Object} config A string to set only the title or a config object
36952  * @param {String} content (optional) Set the HTML content for this panel
36953  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36954  */
36955 Roo.bootstrap.panel.Content = function( config){
36956     
36957     this.tpl = config.tpl || false;
36958     
36959     var el = config.el;
36960     var content = config.content;
36961
36962     if(config.autoCreate){ // xtype is available if this is called from factory
36963         el = Roo.id();
36964     }
36965     this.el = Roo.get(el);
36966     if(!this.el && config && config.autoCreate){
36967         if(typeof config.autoCreate == "object"){
36968             if(!config.autoCreate.id){
36969                 config.autoCreate.id = config.id||el;
36970             }
36971             this.el = Roo.DomHelper.append(document.body,
36972                         config.autoCreate, true);
36973         }else{
36974             var elcfg =  {   tag: "div",
36975                             cls: "roo-layout-inactive-content",
36976                             id: config.id||el
36977                             };
36978             if (config.html) {
36979                 elcfg.html = config.html;
36980                 
36981             }
36982                         
36983             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36984         }
36985     } 
36986     this.closable = false;
36987     this.loaded = false;
36988     this.active = false;
36989    
36990       
36991     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36992         
36993         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36994         
36995         this.wrapEl = this.el; //this.el.wrap();
36996         var ti = [];
36997         if (config.toolbar.items) {
36998             ti = config.toolbar.items ;
36999             delete config.toolbar.items ;
37000         }
37001         
37002         var nitems = [];
37003         this.toolbar.render(this.wrapEl, 'before');
37004         for(var i =0;i < ti.length;i++) {
37005           //  Roo.log(['add child', items[i]]);
37006             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37007         }
37008         this.toolbar.items = nitems;
37009         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37010         delete config.toolbar;
37011         
37012     }
37013     /*
37014     // xtype created footer. - not sure if will work as we normally have to render first..
37015     if (this.footer && !this.footer.el && this.footer.xtype) {
37016         if (!this.wrapEl) {
37017             this.wrapEl = this.el.wrap();
37018         }
37019     
37020         this.footer.container = this.wrapEl.createChild();
37021          
37022         this.footer = Roo.factory(this.footer, Roo);
37023         
37024     }
37025     */
37026     
37027      if(typeof config == "string"){
37028         this.title = config;
37029     }else{
37030         Roo.apply(this, config);
37031     }
37032     
37033     if(this.resizeEl){
37034         this.resizeEl = Roo.get(this.resizeEl, true);
37035     }else{
37036         this.resizeEl = this.el;
37037     }
37038     // handle view.xtype
37039     
37040  
37041     
37042     
37043     this.addEvents({
37044         /**
37045          * @event activate
37046          * Fires when this panel is activated. 
37047          * @param {Roo.ContentPanel} this
37048          */
37049         "activate" : true,
37050         /**
37051          * @event deactivate
37052          * Fires when this panel is activated. 
37053          * @param {Roo.ContentPanel} this
37054          */
37055         "deactivate" : true,
37056
37057         /**
37058          * @event resize
37059          * Fires when this panel is resized if fitToFrame is true.
37060          * @param {Roo.ContentPanel} this
37061          * @param {Number} width The width after any component adjustments
37062          * @param {Number} height The height after any component adjustments
37063          */
37064         "resize" : true,
37065         
37066          /**
37067          * @event render
37068          * Fires when this tab is created
37069          * @param {Roo.ContentPanel} this
37070          */
37071         "render" : true
37072         
37073         
37074         
37075     });
37076     
37077
37078     
37079     
37080     if(this.autoScroll){
37081         this.resizeEl.setStyle("overflow", "auto");
37082     } else {
37083         // fix randome scrolling
37084         //this.el.on('scroll', function() {
37085         //    Roo.log('fix random scolling');
37086         //    this.scrollTo('top',0); 
37087         //});
37088     }
37089     content = content || this.content;
37090     if(content){
37091         this.setContent(content);
37092     }
37093     if(config && config.url){
37094         this.setUrl(this.url, this.params, this.loadOnce);
37095     }
37096     
37097     
37098     
37099     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37100     
37101     if (this.view && typeof(this.view.xtype) != 'undefined') {
37102         this.view.el = this.el.appendChild(document.createElement("div"));
37103         this.view = Roo.factory(this.view); 
37104         this.view.render  &&  this.view.render(false, '');  
37105     }
37106     
37107     
37108     this.fireEvent('render', this);
37109 };
37110
37111 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37112     
37113     tabTip : '',
37114     
37115     setRegion : function(region){
37116         this.region = region;
37117         this.setActiveClass(region && !this.background);
37118     },
37119     
37120     
37121     setActiveClass: function(state)
37122     {
37123         if(state){
37124            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37125            this.el.setStyle('position','relative');
37126         }else{
37127            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37128            this.el.setStyle('position', 'absolute');
37129         } 
37130     },
37131     
37132     /**
37133      * Returns the toolbar for this Panel if one was configured. 
37134      * @return {Roo.Toolbar} 
37135      */
37136     getToolbar : function(){
37137         return this.toolbar;
37138     },
37139     
37140     setActiveState : function(active)
37141     {
37142         this.active = active;
37143         this.setActiveClass(active);
37144         if(!active){
37145             if(this.fireEvent("deactivate", this) === false){
37146                 return false;
37147             }
37148             return true;
37149         }
37150         this.fireEvent("activate", this);
37151         return true;
37152     },
37153     /**
37154      * Updates this panel's element
37155      * @param {String} content The new content
37156      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37157     */
37158     setContent : function(content, loadScripts){
37159         this.el.update(content, loadScripts);
37160     },
37161
37162     ignoreResize : function(w, h){
37163         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37164             return true;
37165         }else{
37166             this.lastSize = {width: w, height: h};
37167             return false;
37168         }
37169     },
37170     /**
37171      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37172      * @return {Roo.UpdateManager} The UpdateManager
37173      */
37174     getUpdateManager : function(){
37175         return this.el.getUpdateManager();
37176     },
37177      /**
37178      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37179      * @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:
37180 <pre><code>
37181 panel.load({
37182     url: "your-url.php",
37183     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37184     callback: yourFunction,
37185     scope: yourObject, //(optional scope)
37186     discardUrl: false,
37187     nocache: false,
37188     text: "Loading...",
37189     timeout: 30,
37190     scripts: false
37191 });
37192 </code></pre>
37193      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37194      * 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.
37195      * @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}
37196      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37197      * @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.
37198      * @return {Roo.ContentPanel} this
37199      */
37200     load : function(){
37201         var um = this.el.getUpdateManager();
37202         um.update.apply(um, arguments);
37203         return this;
37204     },
37205
37206
37207     /**
37208      * 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.
37209      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37210      * @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)
37211      * @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)
37212      * @return {Roo.UpdateManager} The UpdateManager
37213      */
37214     setUrl : function(url, params, loadOnce){
37215         if(this.refreshDelegate){
37216             this.removeListener("activate", this.refreshDelegate);
37217         }
37218         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37219         this.on("activate", this.refreshDelegate);
37220         return this.el.getUpdateManager();
37221     },
37222     
37223     _handleRefresh : function(url, params, loadOnce){
37224         if(!loadOnce || !this.loaded){
37225             var updater = this.el.getUpdateManager();
37226             updater.update(url, params, this._setLoaded.createDelegate(this));
37227         }
37228     },
37229     
37230     _setLoaded : function(){
37231         this.loaded = true;
37232     }, 
37233     
37234     /**
37235      * Returns this panel's id
37236      * @return {String} 
37237      */
37238     getId : function(){
37239         return this.el.id;
37240     },
37241     
37242     /** 
37243      * Returns this panel's element - used by regiosn to add.
37244      * @return {Roo.Element} 
37245      */
37246     getEl : function(){
37247         return this.wrapEl || this.el;
37248     },
37249     
37250    
37251     
37252     adjustForComponents : function(width, height)
37253     {
37254         //Roo.log('adjustForComponents ');
37255         if(this.resizeEl != this.el){
37256             width -= this.el.getFrameWidth('lr');
37257             height -= this.el.getFrameWidth('tb');
37258         }
37259         if(this.toolbar){
37260             var te = this.toolbar.getEl();
37261             te.setWidth(width);
37262             height -= te.getHeight();
37263         }
37264         if(this.footer){
37265             var te = this.footer.getEl();
37266             te.setWidth(width);
37267             height -= te.getHeight();
37268         }
37269         
37270         
37271         if(this.adjustments){
37272             width += this.adjustments[0];
37273             height += this.adjustments[1];
37274         }
37275         return {"width": width, "height": height};
37276     },
37277     
37278     setSize : function(width, height){
37279         if(this.fitToFrame && !this.ignoreResize(width, height)){
37280             if(this.fitContainer && this.resizeEl != this.el){
37281                 this.el.setSize(width, height);
37282             }
37283             var size = this.adjustForComponents(width, height);
37284             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37285             this.fireEvent('resize', this, size.width, size.height);
37286         }
37287     },
37288     
37289     /**
37290      * Returns this panel's title
37291      * @return {String} 
37292      */
37293     getTitle : function(){
37294         
37295         if (typeof(this.title) != 'object') {
37296             return this.title;
37297         }
37298         
37299         var t = '';
37300         for (var k in this.title) {
37301             if (!this.title.hasOwnProperty(k)) {
37302                 continue;
37303             }
37304             
37305             if (k.indexOf('-') >= 0) {
37306                 var s = k.split('-');
37307                 for (var i = 0; i<s.length; i++) {
37308                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37309                 }
37310             } else {
37311                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37312             }
37313         }
37314         return t;
37315     },
37316     
37317     /**
37318      * Set this panel's title
37319      * @param {String} title
37320      */
37321     setTitle : function(title){
37322         this.title = title;
37323         if(this.region){
37324             this.region.updatePanelTitle(this, title);
37325         }
37326     },
37327     
37328     /**
37329      * Returns true is this panel was configured to be closable
37330      * @return {Boolean} 
37331      */
37332     isClosable : function(){
37333         return this.closable;
37334     },
37335     
37336     beforeSlide : function(){
37337         this.el.clip();
37338         this.resizeEl.clip();
37339     },
37340     
37341     afterSlide : function(){
37342         this.el.unclip();
37343         this.resizeEl.unclip();
37344     },
37345     
37346     /**
37347      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37348      *   Will fail silently if the {@link #setUrl} method has not been called.
37349      *   This does not activate the panel, just updates its content.
37350      */
37351     refresh : function(){
37352         if(this.refreshDelegate){
37353            this.loaded = false;
37354            this.refreshDelegate();
37355         }
37356     },
37357     
37358     /**
37359      * Destroys this panel
37360      */
37361     destroy : function(){
37362         this.el.removeAllListeners();
37363         var tempEl = document.createElement("span");
37364         tempEl.appendChild(this.el.dom);
37365         tempEl.innerHTML = "";
37366         this.el.remove();
37367         this.el = null;
37368     },
37369     
37370     /**
37371      * form - if the content panel contains a form - this is a reference to it.
37372      * @type {Roo.form.Form}
37373      */
37374     form : false,
37375     /**
37376      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37377      *    This contains a reference to it.
37378      * @type {Roo.View}
37379      */
37380     view : false,
37381     
37382       /**
37383      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37384      * <pre><code>
37385
37386 layout.addxtype({
37387        xtype : 'Form',
37388        items: [ .... ]
37389    }
37390 );
37391
37392 </code></pre>
37393      * @param {Object} cfg Xtype definition of item to add.
37394      */
37395     
37396     
37397     getChildContainer: function () {
37398         return this.getEl();
37399     }
37400     
37401     
37402     /*
37403         var  ret = new Roo.factory(cfg);
37404         return ret;
37405         
37406         
37407         // add form..
37408         if (cfg.xtype.match(/^Form$/)) {
37409             
37410             var el;
37411             //if (this.footer) {
37412             //    el = this.footer.container.insertSibling(false, 'before');
37413             //} else {
37414                 el = this.el.createChild();
37415             //}
37416
37417             this.form = new  Roo.form.Form(cfg);
37418             
37419             
37420             if ( this.form.allItems.length) {
37421                 this.form.render(el.dom);
37422             }
37423             return this.form;
37424         }
37425         // should only have one of theses..
37426         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37427             // views.. should not be just added - used named prop 'view''
37428             
37429             cfg.el = this.el.appendChild(document.createElement("div"));
37430             // factory?
37431             
37432             var ret = new Roo.factory(cfg);
37433              
37434              ret.render && ret.render(false, ''); // render blank..
37435             this.view = ret;
37436             return ret;
37437         }
37438         return false;
37439     }
37440     \*/
37441 });
37442  
37443 /**
37444  * @class Roo.bootstrap.panel.Grid
37445  * @extends Roo.bootstrap.panel.Content
37446  * @constructor
37447  * Create a new GridPanel.
37448  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37449  * @param {Object} config A the config object
37450   
37451  */
37452
37453
37454
37455 Roo.bootstrap.panel.Grid = function(config)
37456 {
37457     
37458       
37459     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37460         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37461
37462     config.el = this.wrapper;
37463     //this.el = this.wrapper;
37464     
37465       if (config.container) {
37466         // ctor'ed from a Border/panel.grid
37467         
37468         
37469         this.wrapper.setStyle("overflow", "hidden");
37470         this.wrapper.addClass('roo-grid-container');
37471
37472     }
37473     
37474     
37475     if(config.toolbar){
37476         var tool_el = this.wrapper.createChild();    
37477         this.toolbar = Roo.factory(config.toolbar);
37478         var ti = [];
37479         if (config.toolbar.items) {
37480             ti = config.toolbar.items ;
37481             delete config.toolbar.items ;
37482         }
37483         
37484         var nitems = [];
37485         this.toolbar.render(tool_el);
37486         for(var i =0;i < ti.length;i++) {
37487           //  Roo.log(['add child', items[i]]);
37488             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37489         }
37490         this.toolbar.items = nitems;
37491         
37492         delete config.toolbar;
37493     }
37494     
37495     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37496     config.grid.scrollBody = true;;
37497     config.grid.monitorWindowResize = false; // turn off autosizing
37498     config.grid.autoHeight = false;
37499     config.grid.autoWidth = false;
37500     
37501     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37502     
37503     if (config.background) {
37504         // render grid on panel activation (if panel background)
37505         this.on('activate', function(gp) {
37506             if (!gp.grid.rendered) {
37507                 gp.grid.render(this.wrapper);
37508                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37509             }
37510         });
37511             
37512     } else {
37513         this.grid.render(this.wrapper);
37514         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37515
37516     }
37517     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37518     // ??? needed ??? config.el = this.wrapper;
37519     
37520     
37521     
37522   
37523     // xtype created footer. - not sure if will work as we normally have to render first..
37524     if (this.footer && !this.footer.el && this.footer.xtype) {
37525         
37526         var ctr = this.grid.getView().getFooterPanel(true);
37527         this.footer.dataSource = this.grid.dataSource;
37528         this.footer = Roo.factory(this.footer, Roo);
37529         this.footer.render(ctr);
37530         
37531     }
37532     
37533     
37534     
37535     
37536      
37537 };
37538
37539 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37540     getId : function(){
37541         return this.grid.id;
37542     },
37543     
37544     /**
37545      * Returns the grid for this panel
37546      * @return {Roo.bootstrap.Table} 
37547      */
37548     getGrid : function(){
37549         return this.grid;    
37550     },
37551     
37552     setSize : function(width, height){
37553         if(!this.ignoreResize(width, height)){
37554             var grid = this.grid;
37555             var size = this.adjustForComponents(width, height);
37556             var gridel = grid.getGridEl();
37557             gridel.setSize(size.width, size.height);
37558             /*
37559             var thd = grid.getGridEl().select('thead',true).first();
37560             var tbd = grid.getGridEl().select('tbody', true).first();
37561             if (tbd) {
37562                 tbd.setSize(width, height - thd.getHeight());
37563             }
37564             */
37565             grid.autoSize();
37566         }
37567     },
37568      
37569     
37570     
37571     beforeSlide : function(){
37572         this.grid.getView().scroller.clip();
37573     },
37574     
37575     afterSlide : function(){
37576         this.grid.getView().scroller.unclip();
37577     },
37578     
37579     destroy : function(){
37580         this.grid.destroy();
37581         delete this.grid;
37582         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37583     }
37584 });
37585
37586 /**
37587  * @class Roo.bootstrap.panel.Nest
37588  * @extends Roo.bootstrap.panel.Content
37589  * @constructor
37590  * Create a new Panel, that can contain a layout.Border.
37591  * 
37592  * 
37593  * @param {Roo.BorderLayout} layout The layout for this panel
37594  * @param {String/Object} config A string to set only the title or a config object
37595  */
37596 Roo.bootstrap.panel.Nest = function(config)
37597 {
37598     // construct with only one argument..
37599     /* FIXME - implement nicer consturctors
37600     if (layout.layout) {
37601         config = layout;
37602         layout = config.layout;
37603         delete config.layout;
37604     }
37605     if (layout.xtype && !layout.getEl) {
37606         // then layout needs constructing..
37607         layout = Roo.factory(layout, Roo);
37608     }
37609     */
37610     
37611     config.el =  config.layout.getEl();
37612     
37613     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37614     
37615     config.layout.monitorWindowResize = false; // turn off autosizing
37616     this.layout = config.layout;
37617     this.layout.getEl().addClass("roo-layout-nested-layout");
37618     
37619     
37620     
37621     
37622 };
37623
37624 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37625
37626     setSize : function(width, height){
37627         if(!this.ignoreResize(width, height)){
37628             var size = this.adjustForComponents(width, height);
37629             var el = this.layout.getEl();
37630             if (size.height < 1) {
37631                 el.setWidth(size.width);   
37632             } else {
37633                 el.setSize(size.width, size.height);
37634             }
37635             var touch = el.dom.offsetWidth;
37636             this.layout.layout();
37637             // ie requires a double layout on the first pass
37638             if(Roo.isIE && !this.initialized){
37639                 this.initialized = true;
37640                 this.layout.layout();
37641             }
37642         }
37643     },
37644     
37645     // activate all subpanels if not currently active..
37646     
37647     setActiveState : function(active){
37648         this.active = active;
37649         this.setActiveClass(active);
37650         
37651         if(!active){
37652             this.fireEvent("deactivate", this);
37653             return;
37654         }
37655         
37656         this.fireEvent("activate", this);
37657         // not sure if this should happen before or after..
37658         if (!this.layout) {
37659             return; // should not happen..
37660         }
37661         var reg = false;
37662         for (var r in this.layout.regions) {
37663             reg = this.layout.getRegion(r);
37664             if (reg.getActivePanel()) {
37665                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37666                 reg.setActivePanel(reg.getActivePanel());
37667                 continue;
37668             }
37669             if (!reg.panels.length) {
37670                 continue;
37671             }
37672             reg.showPanel(reg.getPanel(0));
37673         }
37674         
37675         
37676         
37677         
37678     },
37679     
37680     /**
37681      * Returns the nested BorderLayout for this panel
37682      * @return {Roo.BorderLayout} 
37683      */
37684     getLayout : function(){
37685         return this.layout;
37686     },
37687     
37688      /**
37689      * Adds a xtype elements to the layout of the nested panel
37690      * <pre><code>
37691
37692 panel.addxtype({
37693        xtype : 'ContentPanel',
37694        region: 'west',
37695        items: [ .... ]
37696    }
37697 );
37698
37699 panel.addxtype({
37700         xtype : 'NestedLayoutPanel',
37701         region: 'west',
37702         layout: {
37703            center: { },
37704            west: { }   
37705         },
37706         items : [ ... list of content panels or nested layout panels.. ]
37707    }
37708 );
37709 </code></pre>
37710      * @param {Object} cfg Xtype definition of item to add.
37711      */
37712     addxtype : function(cfg) {
37713         return this.layout.addxtype(cfg);
37714     
37715     }
37716 });        /*
37717  * Based on:
37718  * Ext JS Library 1.1.1
37719  * Copyright(c) 2006-2007, Ext JS, LLC.
37720  *
37721  * Originally Released Under LGPL - original licence link has changed is not relivant.
37722  *
37723  * Fork - LGPL
37724  * <script type="text/javascript">
37725  */
37726 /**
37727  * @class Roo.TabPanel
37728  * @extends Roo.util.Observable
37729  * A lightweight tab container.
37730  * <br><br>
37731  * Usage:
37732  * <pre><code>
37733 // basic tabs 1, built from existing content
37734 var tabs = new Roo.TabPanel("tabs1");
37735 tabs.addTab("script", "View Script");
37736 tabs.addTab("markup", "View Markup");
37737 tabs.activate("script");
37738
37739 // more advanced tabs, built from javascript
37740 var jtabs = new Roo.TabPanel("jtabs");
37741 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37742
37743 // set up the UpdateManager
37744 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37745 var updater = tab2.getUpdateManager();
37746 updater.setDefaultUrl("ajax1.htm");
37747 tab2.on('activate', updater.refresh, updater, true);
37748
37749 // Use setUrl for Ajax loading
37750 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37751 tab3.setUrl("ajax2.htm", null, true);
37752
37753 // Disabled tab
37754 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37755 tab4.disable();
37756
37757 jtabs.activate("jtabs-1");
37758  * </code></pre>
37759  * @constructor
37760  * Create a new TabPanel.
37761  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37762  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37763  */
37764 Roo.bootstrap.panel.Tabs = function(config){
37765     /**
37766     * The container element for this TabPanel.
37767     * @type Roo.Element
37768     */
37769     this.el = Roo.get(config.el);
37770     delete config.el;
37771     if(config){
37772         if(typeof config == "boolean"){
37773             this.tabPosition = config ? "bottom" : "top";
37774         }else{
37775             Roo.apply(this, config);
37776         }
37777     }
37778     
37779     if(this.tabPosition == "bottom"){
37780         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37781         this.el.addClass("roo-tabs-bottom");
37782     }
37783     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37784     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37785     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37786     if(Roo.isIE){
37787         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37788     }
37789     if(this.tabPosition != "bottom"){
37790         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37791          * @type Roo.Element
37792          */
37793         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37794         this.el.addClass("roo-tabs-top");
37795     }
37796     this.items = [];
37797
37798     this.bodyEl.setStyle("position", "relative");
37799
37800     this.active = null;
37801     this.activateDelegate = this.activate.createDelegate(this);
37802
37803     this.addEvents({
37804         /**
37805          * @event tabchange
37806          * Fires when the active tab changes
37807          * @param {Roo.TabPanel} this
37808          * @param {Roo.TabPanelItem} activePanel The new active tab
37809          */
37810         "tabchange": true,
37811         /**
37812          * @event beforetabchange
37813          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37814          * @param {Roo.TabPanel} this
37815          * @param {Object} e Set cancel to true on this object to cancel the tab change
37816          * @param {Roo.TabPanelItem} tab The tab being changed to
37817          */
37818         "beforetabchange" : true
37819     });
37820
37821     Roo.EventManager.onWindowResize(this.onResize, this);
37822     this.cpad = this.el.getPadding("lr");
37823     this.hiddenCount = 0;
37824
37825
37826     // toolbar on the tabbar support...
37827     if (this.toolbar) {
37828         alert("no toolbar support yet");
37829         this.toolbar  = false;
37830         /*
37831         var tcfg = this.toolbar;
37832         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37833         this.toolbar = new Roo.Toolbar(tcfg);
37834         if (Roo.isSafari) {
37835             var tbl = tcfg.container.child('table', true);
37836             tbl.setAttribute('width', '100%');
37837         }
37838         */
37839         
37840     }
37841    
37842
37843
37844     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37845 };
37846
37847 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37848     /*
37849      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37850      */
37851     tabPosition : "top",
37852     /*
37853      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37854      */
37855     currentTabWidth : 0,
37856     /*
37857      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37858      */
37859     minTabWidth : 40,
37860     /*
37861      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37862      */
37863     maxTabWidth : 250,
37864     /*
37865      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37866      */
37867     preferredTabWidth : 175,
37868     /*
37869      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37870      */
37871     resizeTabs : false,
37872     /*
37873      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37874      */
37875     monitorResize : true,
37876     /*
37877      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37878      */
37879     toolbar : false,
37880
37881     /**
37882      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37883      * @param {String} id The id of the div to use <b>or create</b>
37884      * @param {String} text The text for the tab
37885      * @param {String} content (optional) Content to put in the TabPanelItem body
37886      * @param {Boolean} closable (optional) True to create a close icon on the tab
37887      * @return {Roo.TabPanelItem} The created TabPanelItem
37888      */
37889     addTab : function(id, text, content, closable, tpl)
37890     {
37891         var item = new Roo.bootstrap.panel.TabItem({
37892             panel: this,
37893             id : id,
37894             text : text,
37895             closable : closable,
37896             tpl : tpl
37897         });
37898         this.addTabItem(item);
37899         if(content){
37900             item.setContent(content);
37901         }
37902         return item;
37903     },
37904
37905     /**
37906      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37907      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37908      * @return {Roo.TabPanelItem}
37909      */
37910     getTab : function(id){
37911         return this.items[id];
37912     },
37913
37914     /**
37915      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37916      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37917      */
37918     hideTab : function(id){
37919         var t = this.items[id];
37920         if(!t.isHidden()){
37921            t.setHidden(true);
37922            this.hiddenCount++;
37923            this.autoSizeTabs();
37924         }
37925     },
37926
37927     /**
37928      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37929      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37930      */
37931     unhideTab : function(id){
37932         var t = this.items[id];
37933         if(t.isHidden()){
37934            t.setHidden(false);
37935            this.hiddenCount--;
37936            this.autoSizeTabs();
37937         }
37938     },
37939
37940     /**
37941      * Adds an existing {@link Roo.TabPanelItem}.
37942      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37943      */
37944     addTabItem : function(item){
37945         this.items[item.id] = item;
37946         this.items.push(item);
37947       //  if(this.resizeTabs){
37948     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37949   //         this.autoSizeTabs();
37950 //        }else{
37951 //            item.autoSize();
37952        // }
37953     },
37954
37955     /**
37956      * Removes a {@link Roo.TabPanelItem}.
37957      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37958      */
37959     removeTab : function(id){
37960         var items = this.items;
37961         var tab = items[id];
37962         if(!tab) { return; }
37963         var index = items.indexOf(tab);
37964         if(this.active == tab && items.length > 1){
37965             var newTab = this.getNextAvailable(index);
37966             if(newTab) {
37967                 newTab.activate();
37968             }
37969         }
37970         this.stripEl.dom.removeChild(tab.pnode.dom);
37971         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37972             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37973         }
37974         items.splice(index, 1);
37975         delete this.items[tab.id];
37976         tab.fireEvent("close", tab);
37977         tab.purgeListeners();
37978         this.autoSizeTabs();
37979     },
37980
37981     getNextAvailable : function(start){
37982         var items = this.items;
37983         var index = start;
37984         // look for a next tab that will slide over to
37985         // replace the one being removed
37986         while(index < items.length){
37987             var item = items[++index];
37988             if(item && !item.isHidden()){
37989                 return item;
37990             }
37991         }
37992         // if one isn't found select the previous tab (on the left)
37993         index = start;
37994         while(index >= 0){
37995             var item = items[--index];
37996             if(item && !item.isHidden()){
37997                 return item;
37998             }
37999         }
38000         return null;
38001     },
38002
38003     /**
38004      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38005      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38006      */
38007     disableTab : function(id){
38008         var tab = this.items[id];
38009         if(tab && this.active != tab){
38010             tab.disable();
38011         }
38012     },
38013
38014     /**
38015      * Enables a {@link Roo.TabPanelItem} that is disabled.
38016      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38017      */
38018     enableTab : function(id){
38019         var tab = this.items[id];
38020         tab.enable();
38021     },
38022
38023     /**
38024      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38025      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38026      * @return {Roo.TabPanelItem} The TabPanelItem.
38027      */
38028     activate : function(id){
38029         var tab = this.items[id];
38030         if(!tab){
38031             return null;
38032         }
38033         if(tab == this.active || tab.disabled){
38034             return tab;
38035         }
38036         var e = {};
38037         this.fireEvent("beforetabchange", this, e, tab);
38038         if(e.cancel !== true && !tab.disabled){
38039             if(this.active){
38040                 this.active.hide();
38041             }
38042             this.active = this.items[id];
38043             this.active.show();
38044             this.fireEvent("tabchange", this, this.active);
38045         }
38046         return tab;
38047     },
38048
38049     /**
38050      * Gets the active {@link Roo.TabPanelItem}.
38051      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38052      */
38053     getActiveTab : function(){
38054         return this.active;
38055     },
38056
38057     /**
38058      * Updates the tab body element to fit the height of the container element
38059      * for overflow scrolling
38060      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38061      */
38062     syncHeight : function(targetHeight){
38063         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38064         var bm = this.bodyEl.getMargins();
38065         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38066         this.bodyEl.setHeight(newHeight);
38067         return newHeight;
38068     },
38069
38070     onResize : function(){
38071         if(this.monitorResize){
38072             this.autoSizeTabs();
38073         }
38074     },
38075
38076     /**
38077      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38078      */
38079     beginUpdate : function(){
38080         this.updating = true;
38081     },
38082
38083     /**
38084      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38085      */
38086     endUpdate : function(){
38087         this.updating = false;
38088         this.autoSizeTabs();
38089     },
38090
38091     /**
38092      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38093      */
38094     autoSizeTabs : function(){
38095         var count = this.items.length;
38096         var vcount = count - this.hiddenCount;
38097         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38098             return;
38099         }
38100         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38101         var availWidth = Math.floor(w / vcount);
38102         var b = this.stripBody;
38103         if(b.getWidth() > w){
38104             var tabs = this.items;
38105             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38106             if(availWidth < this.minTabWidth){
38107                 /*if(!this.sleft){    // incomplete scrolling code
38108                     this.createScrollButtons();
38109                 }
38110                 this.showScroll();
38111                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38112             }
38113         }else{
38114             if(this.currentTabWidth < this.preferredTabWidth){
38115                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38116             }
38117         }
38118     },
38119
38120     /**
38121      * Returns the number of tabs in this TabPanel.
38122      * @return {Number}
38123      */
38124      getCount : function(){
38125          return this.items.length;
38126      },
38127
38128     /**
38129      * Resizes all the tabs to the passed width
38130      * @param {Number} The new width
38131      */
38132     setTabWidth : function(width){
38133         this.currentTabWidth = width;
38134         for(var i = 0, len = this.items.length; i < len; i++) {
38135                 if(!this.items[i].isHidden()) {
38136                 this.items[i].setWidth(width);
38137             }
38138         }
38139     },
38140
38141     /**
38142      * Destroys this TabPanel
38143      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38144      */
38145     destroy : function(removeEl){
38146         Roo.EventManager.removeResizeListener(this.onResize, this);
38147         for(var i = 0, len = this.items.length; i < len; i++){
38148             this.items[i].purgeListeners();
38149         }
38150         if(removeEl === true){
38151             this.el.update("");
38152             this.el.remove();
38153         }
38154     },
38155     
38156     createStrip : function(container)
38157     {
38158         var strip = document.createElement("nav");
38159         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38160         container.appendChild(strip);
38161         return strip;
38162     },
38163     
38164     createStripList : function(strip)
38165     {
38166         // div wrapper for retard IE
38167         // returns the "tr" element.
38168         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38169         //'<div class="x-tabs-strip-wrap">'+
38170           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38171           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38172         return strip.firstChild; //.firstChild.firstChild.firstChild;
38173     },
38174     createBody : function(container)
38175     {
38176         var body = document.createElement("div");
38177         Roo.id(body, "tab-body");
38178         //Roo.fly(body).addClass("x-tabs-body");
38179         Roo.fly(body).addClass("tab-content");
38180         container.appendChild(body);
38181         return body;
38182     },
38183     createItemBody :function(bodyEl, id){
38184         var body = Roo.getDom(id);
38185         if(!body){
38186             body = document.createElement("div");
38187             body.id = id;
38188         }
38189         //Roo.fly(body).addClass("x-tabs-item-body");
38190         Roo.fly(body).addClass("tab-pane");
38191          bodyEl.insertBefore(body, bodyEl.firstChild);
38192         return body;
38193     },
38194     /** @private */
38195     createStripElements :  function(stripEl, text, closable, tpl)
38196     {
38197         var td = document.createElement("li"); // was td..
38198         
38199         
38200         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38201         
38202         
38203         stripEl.appendChild(td);
38204         /*if(closable){
38205             td.className = "x-tabs-closable";
38206             if(!this.closeTpl){
38207                 this.closeTpl = new Roo.Template(
38208                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38209                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38210                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38211                 );
38212             }
38213             var el = this.closeTpl.overwrite(td, {"text": text});
38214             var close = el.getElementsByTagName("div")[0];
38215             var inner = el.getElementsByTagName("em")[0];
38216             return {"el": el, "close": close, "inner": inner};
38217         } else {
38218         */
38219         // not sure what this is..
38220 //            if(!this.tabTpl){
38221                 //this.tabTpl = new Roo.Template(
38222                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38223                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38224                 //);
38225 //                this.tabTpl = new Roo.Template(
38226 //                   '<a href="#">' +
38227 //                   '<span unselectable="on"' +
38228 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38229 //                            ' >{text}</span></a>'
38230 //                );
38231 //                
38232 //            }
38233
38234
38235             var template = tpl || this.tabTpl || false;
38236             
38237             if(!template){
38238                 
38239                 template = new Roo.Template(
38240                    '<a href="#">' +
38241                    '<span unselectable="on"' +
38242                             (this.disableTooltips ? '' : ' title="{text}"') +
38243                             ' >{text}</span></a>'
38244                 );
38245             }
38246             
38247             switch (typeof(template)) {
38248                 case 'object' :
38249                     break;
38250                 case 'string' :
38251                     template = new Roo.Template(template);
38252                     break;
38253                 default :
38254                     break;
38255             }
38256             
38257             var el = template.overwrite(td, {"text": text});
38258             
38259             var inner = el.getElementsByTagName("span")[0];
38260             
38261             return {"el": el, "inner": inner};
38262             
38263     }
38264         
38265     
38266 });
38267
38268 /**
38269  * @class Roo.TabPanelItem
38270  * @extends Roo.util.Observable
38271  * Represents an individual item (tab plus body) in a TabPanel.
38272  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38273  * @param {String} id The id of this TabPanelItem
38274  * @param {String} text The text for the tab of this TabPanelItem
38275  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38276  */
38277 Roo.bootstrap.panel.TabItem = function(config){
38278     /**
38279      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38280      * @type Roo.TabPanel
38281      */
38282     this.tabPanel = config.panel;
38283     /**
38284      * The id for this TabPanelItem
38285      * @type String
38286      */
38287     this.id = config.id;
38288     /** @private */
38289     this.disabled = false;
38290     /** @private */
38291     this.text = config.text;
38292     /** @private */
38293     this.loaded = false;
38294     this.closable = config.closable;
38295
38296     /**
38297      * The body element for this TabPanelItem.
38298      * @type Roo.Element
38299      */
38300     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38301     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38302     this.bodyEl.setStyle("display", "block");
38303     this.bodyEl.setStyle("zoom", "1");
38304     //this.hideAction();
38305
38306     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38307     /** @private */
38308     this.el = Roo.get(els.el);
38309     this.inner = Roo.get(els.inner, true);
38310     this.textEl = Roo.get(this.el.dom.firstChild, true);
38311     this.pnode = Roo.get(els.el.parentNode, true);
38312 //    this.el.on("mousedown", this.onTabMouseDown, this);
38313     this.el.on("click", this.onTabClick, this);
38314     /** @private */
38315     if(config.closable){
38316         var c = Roo.get(els.close, true);
38317         c.dom.title = this.closeText;
38318         c.addClassOnOver("close-over");
38319         c.on("click", this.closeClick, this);
38320      }
38321
38322     this.addEvents({
38323          /**
38324          * @event activate
38325          * Fires when this tab becomes the active tab.
38326          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38327          * @param {Roo.TabPanelItem} this
38328          */
38329         "activate": true,
38330         /**
38331          * @event beforeclose
38332          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38333          * @param {Roo.TabPanelItem} this
38334          * @param {Object} e Set cancel to true on this object to cancel the close.
38335          */
38336         "beforeclose": true,
38337         /**
38338          * @event close
38339          * Fires when this tab is closed.
38340          * @param {Roo.TabPanelItem} this
38341          */
38342          "close": true,
38343         /**
38344          * @event deactivate
38345          * Fires when this tab is no longer the active tab.
38346          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38347          * @param {Roo.TabPanelItem} this
38348          */
38349          "deactivate" : true
38350     });
38351     this.hidden = false;
38352
38353     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38354 };
38355
38356 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38357            {
38358     purgeListeners : function(){
38359        Roo.util.Observable.prototype.purgeListeners.call(this);
38360        this.el.removeAllListeners();
38361     },
38362     /**
38363      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38364      */
38365     show : function(){
38366         this.pnode.addClass("active");
38367         this.showAction();
38368         if(Roo.isOpera){
38369             this.tabPanel.stripWrap.repaint();
38370         }
38371         this.fireEvent("activate", this.tabPanel, this);
38372     },
38373
38374     /**
38375      * Returns true if this tab is the active tab.
38376      * @return {Boolean}
38377      */
38378     isActive : function(){
38379         return this.tabPanel.getActiveTab() == this;
38380     },
38381
38382     /**
38383      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38384      */
38385     hide : function(){
38386         this.pnode.removeClass("active");
38387         this.hideAction();
38388         this.fireEvent("deactivate", this.tabPanel, this);
38389     },
38390
38391     hideAction : function(){
38392         this.bodyEl.hide();
38393         this.bodyEl.setStyle("position", "absolute");
38394         this.bodyEl.setLeft("-20000px");
38395         this.bodyEl.setTop("-20000px");
38396     },
38397
38398     showAction : function(){
38399         this.bodyEl.setStyle("position", "relative");
38400         this.bodyEl.setTop("");
38401         this.bodyEl.setLeft("");
38402         this.bodyEl.show();
38403     },
38404
38405     /**
38406      * Set the tooltip for the tab.
38407      * @param {String} tooltip The tab's tooltip
38408      */
38409     setTooltip : function(text){
38410         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38411             this.textEl.dom.qtip = text;
38412             this.textEl.dom.removeAttribute('title');
38413         }else{
38414             this.textEl.dom.title = text;
38415         }
38416     },
38417
38418     onTabClick : function(e){
38419         e.preventDefault();
38420         this.tabPanel.activate(this.id);
38421     },
38422
38423     onTabMouseDown : function(e){
38424         e.preventDefault();
38425         this.tabPanel.activate(this.id);
38426     },
38427 /*
38428     getWidth : function(){
38429         return this.inner.getWidth();
38430     },
38431
38432     setWidth : function(width){
38433         var iwidth = width - this.pnode.getPadding("lr");
38434         this.inner.setWidth(iwidth);
38435         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38436         this.pnode.setWidth(width);
38437     },
38438 */
38439     /**
38440      * Show or hide the tab
38441      * @param {Boolean} hidden True to hide or false to show.
38442      */
38443     setHidden : function(hidden){
38444         this.hidden = hidden;
38445         this.pnode.setStyle("display", hidden ? "none" : "");
38446     },
38447
38448     /**
38449      * Returns true if this tab is "hidden"
38450      * @return {Boolean}
38451      */
38452     isHidden : function(){
38453         return this.hidden;
38454     },
38455
38456     /**
38457      * Returns the text for this tab
38458      * @return {String}
38459      */
38460     getText : function(){
38461         return this.text;
38462     },
38463     /*
38464     autoSize : function(){
38465         //this.el.beginMeasure();
38466         this.textEl.setWidth(1);
38467         /*
38468          *  #2804 [new] Tabs in Roojs
38469          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38470          */
38471         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38472         //this.el.endMeasure();
38473     //},
38474
38475     /**
38476      * Sets the text for the tab (Note: this also sets the tooltip text)
38477      * @param {String} text The tab's text and tooltip
38478      */
38479     setText : function(text){
38480         this.text = text;
38481         this.textEl.update(text);
38482         this.setTooltip(text);
38483         //if(!this.tabPanel.resizeTabs){
38484         //    this.autoSize();
38485         //}
38486     },
38487     /**
38488      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38489      */
38490     activate : function(){
38491         this.tabPanel.activate(this.id);
38492     },
38493
38494     /**
38495      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38496      */
38497     disable : function(){
38498         if(this.tabPanel.active != this){
38499             this.disabled = true;
38500             this.pnode.addClass("disabled");
38501         }
38502     },
38503
38504     /**
38505      * Enables this TabPanelItem if it was previously disabled.
38506      */
38507     enable : function(){
38508         this.disabled = false;
38509         this.pnode.removeClass("disabled");
38510     },
38511
38512     /**
38513      * Sets the content for this TabPanelItem.
38514      * @param {String} content The content
38515      * @param {Boolean} loadScripts true to look for and load scripts
38516      */
38517     setContent : function(content, loadScripts){
38518         this.bodyEl.update(content, loadScripts);
38519     },
38520
38521     /**
38522      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38523      * @return {Roo.UpdateManager} The UpdateManager
38524      */
38525     getUpdateManager : function(){
38526         return this.bodyEl.getUpdateManager();
38527     },
38528
38529     /**
38530      * Set a URL to be used to load the content for this TabPanelItem.
38531      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38532      * @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)
38533      * @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)
38534      * @return {Roo.UpdateManager} The UpdateManager
38535      */
38536     setUrl : function(url, params, loadOnce){
38537         if(this.refreshDelegate){
38538             this.un('activate', this.refreshDelegate);
38539         }
38540         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38541         this.on("activate", this.refreshDelegate);
38542         return this.bodyEl.getUpdateManager();
38543     },
38544
38545     /** @private */
38546     _handleRefresh : function(url, params, loadOnce){
38547         if(!loadOnce || !this.loaded){
38548             var updater = this.bodyEl.getUpdateManager();
38549             updater.update(url, params, this._setLoaded.createDelegate(this));
38550         }
38551     },
38552
38553     /**
38554      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38555      *   Will fail silently if the setUrl method has not been called.
38556      *   This does not activate the panel, just updates its content.
38557      */
38558     refresh : function(){
38559         if(this.refreshDelegate){
38560            this.loaded = false;
38561            this.refreshDelegate();
38562         }
38563     },
38564
38565     /** @private */
38566     _setLoaded : function(){
38567         this.loaded = true;
38568     },
38569
38570     /** @private */
38571     closeClick : function(e){
38572         var o = {};
38573         e.stopEvent();
38574         this.fireEvent("beforeclose", this, o);
38575         if(o.cancel !== true){
38576             this.tabPanel.removeTab(this.id);
38577         }
38578     },
38579     /**
38580      * The text displayed in the tooltip for the close icon.
38581      * @type String
38582      */
38583     closeText : "Close this tab"
38584 });
38585 /**
38586 *    This script refer to:
38587 *    Title: International Telephone Input
38588 *    Author: Jack O'Connor
38589 *    Code version:  v12.1.12
38590 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38591 **/
38592
38593 Roo.bootstrap.PhoneInputData = function() {
38594     var d = [
38595       [
38596         "Afghanistan (‫افغانستان‬‎)",
38597         "af",
38598         "93"
38599       ],
38600       [
38601         "Albania (Shqipëri)",
38602         "al",
38603         "355"
38604       ],
38605       [
38606         "Algeria (‫الجزائر‬‎)",
38607         "dz",
38608         "213"
38609       ],
38610       [
38611         "American Samoa",
38612         "as",
38613         "1684"
38614       ],
38615       [
38616         "Andorra",
38617         "ad",
38618         "376"
38619       ],
38620       [
38621         "Angola",
38622         "ao",
38623         "244"
38624       ],
38625       [
38626         "Anguilla",
38627         "ai",
38628         "1264"
38629       ],
38630       [
38631         "Antigua and Barbuda",
38632         "ag",
38633         "1268"
38634       ],
38635       [
38636         "Argentina",
38637         "ar",
38638         "54"
38639       ],
38640       [
38641         "Armenia (Հայաստան)",
38642         "am",
38643         "374"
38644       ],
38645       [
38646         "Aruba",
38647         "aw",
38648         "297"
38649       ],
38650       [
38651         "Australia",
38652         "au",
38653         "61",
38654         0
38655       ],
38656       [
38657         "Austria (Österreich)",
38658         "at",
38659         "43"
38660       ],
38661       [
38662         "Azerbaijan (Azərbaycan)",
38663         "az",
38664         "994"
38665       ],
38666       [
38667         "Bahamas",
38668         "bs",
38669         "1242"
38670       ],
38671       [
38672         "Bahrain (‫البحرين‬‎)",
38673         "bh",
38674         "973"
38675       ],
38676       [
38677         "Bangladesh (বাংলাদেশ)",
38678         "bd",
38679         "880"
38680       ],
38681       [
38682         "Barbados",
38683         "bb",
38684         "1246"
38685       ],
38686       [
38687         "Belarus (Беларусь)",
38688         "by",
38689         "375"
38690       ],
38691       [
38692         "Belgium (België)",
38693         "be",
38694         "32"
38695       ],
38696       [
38697         "Belize",
38698         "bz",
38699         "501"
38700       ],
38701       [
38702         "Benin (Bénin)",
38703         "bj",
38704         "229"
38705       ],
38706       [
38707         "Bermuda",
38708         "bm",
38709         "1441"
38710       ],
38711       [
38712         "Bhutan (འབྲུག)",
38713         "bt",
38714         "975"
38715       ],
38716       [
38717         "Bolivia",
38718         "bo",
38719         "591"
38720       ],
38721       [
38722         "Bosnia and Herzegovina (Босна и Херцеговина)",
38723         "ba",
38724         "387"
38725       ],
38726       [
38727         "Botswana",
38728         "bw",
38729         "267"
38730       ],
38731       [
38732         "Brazil (Brasil)",
38733         "br",
38734         "55"
38735       ],
38736       [
38737         "British Indian Ocean Territory",
38738         "io",
38739         "246"
38740       ],
38741       [
38742         "British Virgin Islands",
38743         "vg",
38744         "1284"
38745       ],
38746       [
38747         "Brunei",
38748         "bn",
38749         "673"
38750       ],
38751       [
38752         "Bulgaria (България)",
38753         "bg",
38754         "359"
38755       ],
38756       [
38757         "Burkina Faso",
38758         "bf",
38759         "226"
38760       ],
38761       [
38762         "Burundi (Uburundi)",
38763         "bi",
38764         "257"
38765       ],
38766       [
38767         "Cambodia (កម្ពុជា)",
38768         "kh",
38769         "855"
38770       ],
38771       [
38772         "Cameroon (Cameroun)",
38773         "cm",
38774         "237"
38775       ],
38776       [
38777         "Canada",
38778         "ca",
38779         "1",
38780         1,
38781         ["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"]
38782       ],
38783       [
38784         "Cape Verde (Kabu Verdi)",
38785         "cv",
38786         "238"
38787       ],
38788       [
38789         "Caribbean Netherlands",
38790         "bq",
38791         "599",
38792         1
38793       ],
38794       [
38795         "Cayman Islands",
38796         "ky",
38797         "1345"
38798       ],
38799       [
38800         "Central African Republic (République centrafricaine)",
38801         "cf",
38802         "236"
38803       ],
38804       [
38805         "Chad (Tchad)",
38806         "td",
38807         "235"
38808       ],
38809       [
38810         "Chile",
38811         "cl",
38812         "56"
38813       ],
38814       [
38815         "China (中国)",
38816         "cn",
38817         "86"
38818       ],
38819       [
38820         "Christmas Island",
38821         "cx",
38822         "61",
38823         2
38824       ],
38825       [
38826         "Cocos (Keeling) Islands",
38827         "cc",
38828         "61",
38829         1
38830       ],
38831       [
38832         "Colombia",
38833         "co",
38834         "57"
38835       ],
38836       [
38837         "Comoros (‫جزر القمر‬‎)",
38838         "km",
38839         "269"
38840       ],
38841       [
38842         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38843         "cd",
38844         "243"
38845       ],
38846       [
38847         "Congo (Republic) (Congo-Brazzaville)",
38848         "cg",
38849         "242"
38850       ],
38851       [
38852         "Cook Islands",
38853         "ck",
38854         "682"
38855       ],
38856       [
38857         "Costa Rica",
38858         "cr",
38859         "506"
38860       ],
38861       [
38862         "Côte d’Ivoire",
38863         "ci",
38864         "225"
38865       ],
38866       [
38867         "Croatia (Hrvatska)",
38868         "hr",
38869         "385"
38870       ],
38871       [
38872         "Cuba",
38873         "cu",
38874         "53"
38875       ],
38876       [
38877         "Curaçao",
38878         "cw",
38879         "599",
38880         0
38881       ],
38882       [
38883         "Cyprus (Κύπρος)",
38884         "cy",
38885         "357"
38886       ],
38887       [
38888         "Czech Republic (Česká republika)",
38889         "cz",
38890         "420"
38891       ],
38892       [
38893         "Denmark (Danmark)",
38894         "dk",
38895         "45"
38896       ],
38897       [
38898         "Djibouti",
38899         "dj",
38900         "253"
38901       ],
38902       [
38903         "Dominica",
38904         "dm",
38905         "1767"
38906       ],
38907       [
38908         "Dominican Republic (República Dominicana)",
38909         "do",
38910         "1",
38911         2,
38912         ["809", "829", "849"]
38913       ],
38914       [
38915         "Ecuador",
38916         "ec",
38917         "593"
38918       ],
38919       [
38920         "Egypt (‫مصر‬‎)",
38921         "eg",
38922         "20"
38923       ],
38924       [
38925         "El Salvador",
38926         "sv",
38927         "503"
38928       ],
38929       [
38930         "Equatorial Guinea (Guinea Ecuatorial)",
38931         "gq",
38932         "240"
38933       ],
38934       [
38935         "Eritrea",
38936         "er",
38937         "291"
38938       ],
38939       [
38940         "Estonia (Eesti)",
38941         "ee",
38942         "372"
38943       ],
38944       [
38945         "Ethiopia",
38946         "et",
38947         "251"
38948       ],
38949       [
38950         "Falkland Islands (Islas Malvinas)",
38951         "fk",
38952         "500"
38953       ],
38954       [
38955         "Faroe Islands (Føroyar)",
38956         "fo",
38957         "298"
38958       ],
38959       [
38960         "Fiji",
38961         "fj",
38962         "679"
38963       ],
38964       [
38965         "Finland (Suomi)",
38966         "fi",
38967         "358",
38968         0
38969       ],
38970       [
38971         "France",
38972         "fr",
38973         "33"
38974       ],
38975       [
38976         "French Guiana (Guyane française)",
38977         "gf",
38978         "594"
38979       ],
38980       [
38981         "French Polynesia (Polynésie française)",
38982         "pf",
38983         "689"
38984       ],
38985       [
38986         "Gabon",
38987         "ga",
38988         "241"
38989       ],
38990       [
38991         "Gambia",
38992         "gm",
38993         "220"
38994       ],
38995       [
38996         "Georgia (საქართველო)",
38997         "ge",
38998         "995"
38999       ],
39000       [
39001         "Germany (Deutschland)",
39002         "de",
39003         "49"
39004       ],
39005       [
39006         "Ghana (Gaana)",
39007         "gh",
39008         "233"
39009       ],
39010       [
39011         "Gibraltar",
39012         "gi",
39013         "350"
39014       ],
39015       [
39016         "Greece (Ελλάδα)",
39017         "gr",
39018         "30"
39019       ],
39020       [
39021         "Greenland (Kalaallit Nunaat)",
39022         "gl",
39023         "299"
39024       ],
39025       [
39026         "Grenada",
39027         "gd",
39028         "1473"
39029       ],
39030       [
39031         "Guadeloupe",
39032         "gp",
39033         "590",
39034         0
39035       ],
39036       [
39037         "Guam",
39038         "gu",
39039         "1671"
39040       ],
39041       [
39042         "Guatemala",
39043         "gt",
39044         "502"
39045       ],
39046       [
39047         "Guernsey",
39048         "gg",
39049         "44",
39050         1
39051       ],
39052       [
39053         "Guinea (Guinée)",
39054         "gn",
39055         "224"
39056       ],
39057       [
39058         "Guinea-Bissau (Guiné Bissau)",
39059         "gw",
39060         "245"
39061       ],
39062       [
39063         "Guyana",
39064         "gy",
39065         "592"
39066       ],
39067       [
39068         "Haiti",
39069         "ht",
39070         "509"
39071       ],
39072       [
39073         "Honduras",
39074         "hn",
39075         "504"
39076       ],
39077       [
39078         "Hong Kong (香港)",
39079         "hk",
39080         "852"
39081       ],
39082       [
39083         "Hungary (Magyarország)",
39084         "hu",
39085         "36"
39086       ],
39087       [
39088         "Iceland (Ísland)",
39089         "is",
39090         "354"
39091       ],
39092       [
39093         "India (भारत)",
39094         "in",
39095         "91"
39096       ],
39097       [
39098         "Indonesia",
39099         "id",
39100         "62"
39101       ],
39102       [
39103         "Iran (‫ایران‬‎)",
39104         "ir",
39105         "98"
39106       ],
39107       [
39108         "Iraq (‫العراق‬‎)",
39109         "iq",
39110         "964"
39111       ],
39112       [
39113         "Ireland",
39114         "ie",
39115         "353"
39116       ],
39117       [
39118         "Isle of Man",
39119         "im",
39120         "44",
39121         2
39122       ],
39123       [
39124         "Israel (‫ישראל‬‎)",
39125         "il",
39126         "972"
39127       ],
39128       [
39129         "Italy (Italia)",
39130         "it",
39131         "39",
39132         0
39133       ],
39134       [
39135         "Jamaica",
39136         "jm",
39137         "1876"
39138       ],
39139       [
39140         "Japan (日本)",
39141         "jp",
39142         "81"
39143       ],
39144       [
39145         "Jersey",
39146         "je",
39147         "44",
39148         3
39149       ],
39150       [
39151         "Jordan (‫الأردن‬‎)",
39152         "jo",
39153         "962"
39154       ],
39155       [
39156         "Kazakhstan (Казахстан)",
39157         "kz",
39158         "7",
39159         1
39160       ],
39161       [
39162         "Kenya",
39163         "ke",
39164         "254"
39165       ],
39166       [
39167         "Kiribati",
39168         "ki",
39169         "686"
39170       ],
39171       [
39172         "Kosovo",
39173         "xk",
39174         "383"
39175       ],
39176       [
39177         "Kuwait (‫الكويت‬‎)",
39178         "kw",
39179         "965"
39180       ],
39181       [
39182         "Kyrgyzstan (Кыргызстан)",
39183         "kg",
39184         "996"
39185       ],
39186       [
39187         "Laos (ລາວ)",
39188         "la",
39189         "856"
39190       ],
39191       [
39192         "Latvia (Latvija)",
39193         "lv",
39194         "371"
39195       ],
39196       [
39197         "Lebanon (‫لبنان‬‎)",
39198         "lb",
39199         "961"
39200       ],
39201       [
39202         "Lesotho",
39203         "ls",
39204         "266"
39205       ],
39206       [
39207         "Liberia",
39208         "lr",
39209         "231"
39210       ],
39211       [
39212         "Libya (‫ليبيا‬‎)",
39213         "ly",
39214         "218"
39215       ],
39216       [
39217         "Liechtenstein",
39218         "li",
39219         "423"
39220       ],
39221       [
39222         "Lithuania (Lietuva)",
39223         "lt",
39224         "370"
39225       ],
39226       [
39227         "Luxembourg",
39228         "lu",
39229         "352"
39230       ],
39231       [
39232         "Macau (澳門)",
39233         "mo",
39234         "853"
39235       ],
39236       [
39237         "Macedonia (FYROM) (Македонија)",
39238         "mk",
39239         "389"
39240       ],
39241       [
39242         "Madagascar (Madagasikara)",
39243         "mg",
39244         "261"
39245       ],
39246       [
39247         "Malawi",
39248         "mw",
39249         "265"
39250       ],
39251       [
39252         "Malaysia",
39253         "my",
39254         "60"
39255       ],
39256       [
39257         "Maldives",
39258         "mv",
39259         "960"
39260       ],
39261       [
39262         "Mali",
39263         "ml",
39264         "223"
39265       ],
39266       [
39267         "Malta",
39268         "mt",
39269         "356"
39270       ],
39271       [
39272         "Marshall Islands",
39273         "mh",
39274         "692"
39275       ],
39276       [
39277         "Martinique",
39278         "mq",
39279         "596"
39280       ],
39281       [
39282         "Mauritania (‫موريتانيا‬‎)",
39283         "mr",
39284         "222"
39285       ],
39286       [
39287         "Mauritius (Moris)",
39288         "mu",
39289         "230"
39290       ],
39291       [
39292         "Mayotte",
39293         "yt",
39294         "262",
39295         1
39296       ],
39297       [
39298         "Mexico (México)",
39299         "mx",
39300         "52"
39301       ],
39302       [
39303         "Micronesia",
39304         "fm",
39305         "691"
39306       ],
39307       [
39308         "Moldova (Republica Moldova)",
39309         "md",
39310         "373"
39311       ],
39312       [
39313         "Monaco",
39314         "mc",
39315         "377"
39316       ],
39317       [
39318         "Mongolia (Монгол)",
39319         "mn",
39320         "976"
39321       ],
39322       [
39323         "Montenegro (Crna Gora)",
39324         "me",
39325         "382"
39326       ],
39327       [
39328         "Montserrat",
39329         "ms",
39330         "1664"
39331       ],
39332       [
39333         "Morocco (‫المغرب‬‎)",
39334         "ma",
39335         "212",
39336         0
39337       ],
39338       [
39339         "Mozambique (Moçambique)",
39340         "mz",
39341         "258"
39342       ],
39343       [
39344         "Myanmar (Burma) (မြန်မာ)",
39345         "mm",
39346         "95"
39347       ],
39348       [
39349         "Namibia (Namibië)",
39350         "na",
39351         "264"
39352       ],
39353       [
39354         "Nauru",
39355         "nr",
39356         "674"
39357       ],
39358       [
39359         "Nepal (नेपाल)",
39360         "np",
39361         "977"
39362       ],
39363       [
39364         "Netherlands (Nederland)",
39365         "nl",
39366         "31"
39367       ],
39368       [
39369         "New Caledonia (Nouvelle-Calédonie)",
39370         "nc",
39371         "687"
39372       ],
39373       [
39374         "New Zealand",
39375         "nz",
39376         "64"
39377       ],
39378       [
39379         "Nicaragua",
39380         "ni",
39381         "505"
39382       ],
39383       [
39384         "Niger (Nijar)",
39385         "ne",
39386         "227"
39387       ],
39388       [
39389         "Nigeria",
39390         "ng",
39391         "234"
39392       ],
39393       [
39394         "Niue",
39395         "nu",
39396         "683"
39397       ],
39398       [
39399         "Norfolk Island",
39400         "nf",
39401         "672"
39402       ],
39403       [
39404         "North Korea (조선 민주주의 인민 공화국)",
39405         "kp",
39406         "850"
39407       ],
39408       [
39409         "Northern Mariana Islands",
39410         "mp",
39411         "1670"
39412       ],
39413       [
39414         "Norway (Norge)",
39415         "no",
39416         "47",
39417         0
39418       ],
39419       [
39420         "Oman (‫عُمان‬‎)",
39421         "om",
39422         "968"
39423       ],
39424       [
39425         "Pakistan (‫پاکستان‬‎)",
39426         "pk",
39427         "92"
39428       ],
39429       [
39430         "Palau",
39431         "pw",
39432         "680"
39433       ],
39434       [
39435         "Palestine (‫فلسطين‬‎)",
39436         "ps",
39437         "970"
39438       ],
39439       [
39440         "Panama (Panamá)",
39441         "pa",
39442         "507"
39443       ],
39444       [
39445         "Papua New Guinea",
39446         "pg",
39447         "675"
39448       ],
39449       [
39450         "Paraguay",
39451         "py",
39452         "595"
39453       ],
39454       [
39455         "Peru (Perú)",
39456         "pe",
39457         "51"
39458       ],
39459       [
39460         "Philippines",
39461         "ph",
39462         "63"
39463       ],
39464       [
39465         "Poland (Polska)",
39466         "pl",
39467         "48"
39468       ],
39469       [
39470         "Portugal",
39471         "pt",
39472         "351"
39473       ],
39474       [
39475         "Puerto Rico",
39476         "pr",
39477         "1",
39478         3,
39479         ["787", "939"]
39480       ],
39481       [
39482         "Qatar (‫قطر‬‎)",
39483         "qa",
39484         "974"
39485       ],
39486       [
39487         "Réunion (La Réunion)",
39488         "re",
39489         "262",
39490         0
39491       ],
39492       [
39493         "Romania (România)",
39494         "ro",
39495         "40"
39496       ],
39497       [
39498         "Russia (Россия)",
39499         "ru",
39500         "7",
39501         0
39502       ],
39503       [
39504         "Rwanda",
39505         "rw",
39506         "250"
39507       ],
39508       [
39509         "Saint Barthélemy",
39510         "bl",
39511         "590",
39512         1
39513       ],
39514       [
39515         "Saint Helena",
39516         "sh",
39517         "290"
39518       ],
39519       [
39520         "Saint Kitts and Nevis",
39521         "kn",
39522         "1869"
39523       ],
39524       [
39525         "Saint Lucia",
39526         "lc",
39527         "1758"
39528       ],
39529       [
39530         "Saint Martin (Saint-Martin (partie française))",
39531         "mf",
39532         "590",
39533         2
39534       ],
39535       [
39536         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39537         "pm",
39538         "508"
39539       ],
39540       [
39541         "Saint Vincent and the Grenadines",
39542         "vc",
39543         "1784"
39544       ],
39545       [
39546         "Samoa",
39547         "ws",
39548         "685"
39549       ],
39550       [
39551         "San Marino",
39552         "sm",
39553         "378"
39554       ],
39555       [
39556         "São Tomé and Príncipe (São Tomé e Príncipe)",
39557         "st",
39558         "239"
39559       ],
39560       [
39561         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39562         "sa",
39563         "966"
39564       ],
39565       [
39566         "Senegal (Sénégal)",
39567         "sn",
39568         "221"
39569       ],
39570       [
39571         "Serbia (Србија)",
39572         "rs",
39573         "381"
39574       ],
39575       [
39576         "Seychelles",
39577         "sc",
39578         "248"
39579       ],
39580       [
39581         "Sierra Leone",
39582         "sl",
39583         "232"
39584       ],
39585       [
39586         "Singapore",
39587         "sg",
39588         "65"
39589       ],
39590       [
39591         "Sint Maarten",
39592         "sx",
39593         "1721"
39594       ],
39595       [
39596         "Slovakia (Slovensko)",
39597         "sk",
39598         "421"
39599       ],
39600       [
39601         "Slovenia (Slovenija)",
39602         "si",
39603         "386"
39604       ],
39605       [
39606         "Solomon Islands",
39607         "sb",
39608         "677"
39609       ],
39610       [
39611         "Somalia (Soomaaliya)",
39612         "so",
39613         "252"
39614       ],
39615       [
39616         "South Africa",
39617         "za",
39618         "27"
39619       ],
39620       [
39621         "South Korea (대한민국)",
39622         "kr",
39623         "82"
39624       ],
39625       [
39626         "South Sudan (‫جنوب السودان‬‎)",
39627         "ss",
39628         "211"
39629       ],
39630       [
39631         "Spain (España)",
39632         "es",
39633         "34"
39634       ],
39635       [
39636         "Sri Lanka (ශ්‍රී ලංකාව)",
39637         "lk",
39638         "94"
39639       ],
39640       [
39641         "Sudan (‫السودان‬‎)",
39642         "sd",
39643         "249"
39644       ],
39645       [
39646         "Suriname",
39647         "sr",
39648         "597"
39649       ],
39650       [
39651         "Svalbard and Jan Mayen",
39652         "sj",
39653         "47",
39654         1
39655       ],
39656       [
39657         "Swaziland",
39658         "sz",
39659         "268"
39660       ],
39661       [
39662         "Sweden (Sverige)",
39663         "se",
39664         "46"
39665       ],
39666       [
39667         "Switzerland (Schweiz)",
39668         "ch",
39669         "41"
39670       ],
39671       [
39672         "Syria (‫سوريا‬‎)",
39673         "sy",
39674         "963"
39675       ],
39676       [
39677         "Taiwan (台灣)",
39678         "tw",
39679         "886"
39680       ],
39681       [
39682         "Tajikistan",
39683         "tj",
39684         "992"
39685       ],
39686       [
39687         "Tanzania",
39688         "tz",
39689         "255"
39690       ],
39691       [
39692         "Thailand (ไทย)",
39693         "th",
39694         "66"
39695       ],
39696       [
39697         "Timor-Leste",
39698         "tl",
39699         "670"
39700       ],
39701       [
39702         "Togo",
39703         "tg",
39704         "228"
39705       ],
39706       [
39707         "Tokelau",
39708         "tk",
39709         "690"
39710       ],
39711       [
39712         "Tonga",
39713         "to",
39714         "676"
39715       ],
39716       [
39717         "Trinidad and Tobago",
39718         "tt",
39719         "1868"
39720       ],
39721       [
39722         "Tunisia (‫تونس‬‎)",
39723         "tn",
39724         "216"
39725       ],
39726       [
39727         "Turkey (Türkiye)",
39728         "tr",
39729         "90"
39730       ],
39731       [
39732         "Turkmenistan",
39733         "tm",
39734         "993"
39735       ],
39736       [
39737         "Turks and Caicos Islands",
39738         "tc",
39739         "1649"
39740       ],
39741       [
39742         "Tuvalu",
39743         "tv",
39744         "688"
39745       ],
39746       [
39747         "U.S. Virgin Islands",
39748         "vi",
39749         "1340"
39750       ],
39751       [
39752         "Uganda",
39753         "ug",
39754         "256"
39755       ],
39756       [
39757         "Ukraine (Україна)",
39758         "ua",
39759         "380"
39760       ],
39761       [
39762         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39763         "ae",
39764         "971"
39765       ],
39766       [
39767         "United Kingdom",
39768         "gb",
39769         "44",
39770         0
39771       ],
39772       [
39773         "United States",
39774         "us",
39775         "1",
39776         0
39777       ],
39778       [
39779         "Uruguay",
39780         "uy",
39781         "598"
39782       ],
39783       [
39784         "Uzbekistan (Oʻzbekiston)",
39785         "uz",
39786         "998"
39787       ],
39788       [
39789         "Vanuatu",
39790         "vu",
39791         "678"
39792       ],
39793       [
39794         "Vatican City (Città del Vaticano)",
39795         "va",
39796         "39",
39797         1
39798       ],
39799       [
39800         "Venezuela",
39801         "ve",
39802         "58"
39803       ],
39804       [
39805         "Vietnam (Việt Nam)",
39806         "vn",
39807         "84"
39808       ],
39809       [
39810         "Wallis and Futuna (Wallis-et-Futuna)",
39811         "wf",
39812         "681"
39813       ],
39814       [
39815         "Western Sahara (‫الصحراء الغربية‬‎)",
39816         "eh",
39817         "212",
39818         1
39819       ],
39820       [
39821         "Yemen (‫اليمن‬‎)",
39822         "ye",
39823         "967"
39824       ],
39825       [
39826         "Zambia",
39827         "zm",
39828         "260"
39829       ],
39830       [
39831         "Zimbabwe",
39832         "zw",
39833         "263"
39834       ],
39835       [
39836         "Åland Islands",
39837         "ax",
39838         "358",
39839         1
39840       ]
39841   ];
39842   
39843   return d;
39844 }/**
39845 *    This script refer to:
39846 *    Title: International Telephone Input
39847 *    Author: Jack O'Connor
39848 *    Code version:  v12.1.12
39849 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39850 **/
39851
39852 /**
39853  * @class Roo.bootstrap.PhoneInput
39854  * @extends Roo.bootstrap.TriggerField
39855  * An input with International dial-code selection
39856  
39857  * @cfg {String} defaultDialCode default '+852'
39858  * @cfg {Array} preferedCountries default []
39859   
39860  * @constructor
39861  * Create a new PhoneInput.
39862  * @param {Object} config Configuration options
39863  */
39864
39865 Roo.bootstrap.PhoneInput = function(config) {
39866     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39867 };
39868
39869 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39870         
39871         listWidth: undefined,
39872         
39873         selectedClass: 'active',
39874         
39875         invalidClass : "has-warning",
39876         
39877         validClass: 'has-success',
39878         
39879         allowed: '0123456789',
39880         
39881         max_length: 15,
39882         
39883         /**
39884          * @cfg {String} defaultDialCode The default dial code when initializing the input
39885          */
39886         defaultDialCode: '+852',
39887         
39888         /**
39889          * @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
39890          */
39891         preferedCountries: false,
39892         
39893         getAutoCreate : function()
39894         {
39895             var data = Roo.bootstrap.PhoneInputData();
39896             var align = this.labelAlign || this.parentLabelAlign();
39897             var id = Roo.id();
39898             
39899             this.allCountries = [];
39900             this.dialCodeMapping = [];
39901             
39902             for (var i = 0; i < data.length; i++) {
39903               var c = data[i];
39904               this.allCountries[i] = {
39905                 name: c[0],
39906                 iso2: c[1],
39907                 dialCode: c[2],
39908                 priority: c[3] || 0,
39909                 areaCodes: c[4] || null
39910               };
39911               this.dialCodeMapping[c[2]] = {
39912                   name: c[0],
39913                   iso2: c[1],
39914                   priority: c[3] || 0,
39915                   areaCodes: c[4] || null
39916               };
39917             }
39918             
39919             var cfg = {
39920                 cls: 'form-group',
39921                 cn: []
39922             };
39923             
39924             var input =  {
39925                 tag: 'input',
39926                 id : id,
39927                 maxlength: this.max_length,
39928                 cls : 'form-control tel-input',
39929                 autocomplete: 'new-password'
39930             };
39931             
39932             var hiddenInput = {
39933                 tag: 'input',
39934                 type: 'hidden',
39935                 cls: 'hidden-tel-input'
39936             };
39937             
39938             if (this.name) {
39939                 hiddenInput.name = this.name;
39940             }
39941             
39942             if (this.disabled) {
39943                 input.disabled = true;
39944             }
39945             
39946             var flag_container = {
39947                 tag: 'div',
39948                 cls: 'flag-box',
39949                 cn: [
39950                     {
39951                         tag: 'div',
39952                         cls: 'flag'
39953                     },
39954                     {
39955                         tag: 'div',
39956                         cls: 'caret'
39957                     }
39958                 ]
39959             };
39960             
39961             var box = {
39962                 tag: 'div',
39963                 cls: this.hasFeedback ? 'has-feedback' : '',
39964                 cn: [
39965                     hiddenInput,
39966                     input,
39967                     {
39968                         tag: 'input',
39969                         cls: 'dial-code-holder',
39970                         disabled: true
39971                     }
39972                 ]
39973             };
39974             
39975             var container = {
39976                 cls: 'roo-select2-container input-group',
39977                 cn: [
39978                     flag_container,
39979                     box
39980                 ]
39981             };
39982             
39983             if (this.fieldLabel.length) {
39984                 var indicator = {
39985                     tag: 'i',
39986                     tooltip: 'This field is required'
39987                 };
39988                 
39989                 var label = {
39990                     tag: 'label',
39991                     'for':  id,
39992                     cls: 'control-label',
39993                     cn: []
39994                 };
39995                 
39996                 var label_text = {
39997                     tag: 'span',
39998                     html: this.fieldLabel
39999                 };
40000                 
40001                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40002                 label.cn = [
40003                     indicator,
40004                     label_text
40005                 ];
40006                 
40007                 if(this.indicatorpos == 'right') {
40008                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40009                     label.cn = [
40010                         label_text,
40011                         indicator
40012                     ];
40013                 }
40014                 
40015                 if(align == 'left') {
40016                     container = {
40017                         tag: 'div',
40018                         cn: [
40019                             container
40020                         ]
40021                     };
40022                     
40023                     if(this.labelWidth > 12){
40024                         label.style = "width: " + this.labelWidth + 'px';
40025                     }
40026                     if(this.labelWidth < 13 && this.labelmd == 0){
40027                         this.labelmd = this.labelWidth;
40028                     }
40029                     if(this.labellg > 0){
40030                         label.cls += ' col-lg-' + this.labellg;
40031                         input.cls += ' col-lg-' + (12 - this.labellg);
40032                     }
40033                     if(this.labelmd > 0){
40034                         label.cls += ' col-md-' + this.labelmd;
40035                         container.cls += ' col-md-' + (12 - this.labelmd);
40036                     }
40037                     if(this.labelsm > 0){
40038                         label.cls += ' col-sm-' + this.labelsm;
40039                         container.cls += ' col-sm-' + (12 - this.labelsm);
40040                     }
40041                     if(this.labelxs > 0){
40042                         label.cls += ' col-xs-' + this.labelxs;
40043                         container.cls += ' col-xs-' + (12 - this.labelxs);
40044                     }
40045                 }
40046             }
40047             
40048             cfg.cn = [
40049                 label,
40050                 container
40051             ];
40052             
40053             var settings = this;
40054             
40055             ['xs','sm','md','lg'].map(function(size){
40056                 if (settings[size]) {
40057                     cfg.cls += ' col-' + size + '-' + settings[size];
40058                 }
40059             });
40060             
40061             this.store = new Roo.data.Store({
40062                 proxy : new Roo.data.MemoryProxy({}),
40063                 reader : new Roo.data.JsonReader({
40064                     fields : [
40065                         {
40066                             'name' : 'name',
40067                             'type' : 'string'
40068                         },
40069                         {
40070                             'name' : 'iso2',
40071                             'type' : 'string'
40072                         },
40073                         {
40074                             'name' : 'dialCode',
40075                             'type' : 'string'
40076                         },
40077                         {
40078                             'name' : 'priority',
40079                             'type' : 'string'
40080                         },
40081                         {
40082                             'name' : 'areaCodes',
40083                             'type' : 'string'
40084                         }
40085                     ]
40086                 })
40087             });
40088             
40089             if(!this.preferedCountries) {
40090                 this.preferedCountries = [
40091                     'hk',
40092                     'gb',
40093                     'us'
40094                 ];
40095             }
40096             
40097             var p = this.preferedCountries.reverse();
40098             
40099             if(p) {
40100                 for (var i = 0; i < p.length; i++) {
40101                     for (var j = 0; j < this.allCountries.length; j++) {
40102                         if(this.allCountries[j].iso2 == p[i]) {
40103                             var t = this.allCountries[j];
40104                             this.allCountries.splice(j,1);
40105                             this.allCountries.unshift(t);
40106                         }
40107                     } 
40108                 }
40109             }
40110             
40111             this.store.proxy.data = {
40112                 success: true,
40113                 data: this.allCountries
40114             };
40115             
40116             return cfg;
40117         },
40118         
40119         initEvents : function()
40120         {
40121             this.createList();
40122             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40123             
40124             this.indicator = this.indicatorEl();
40125             this.flag = this.flagEl();
40126             this.dialCodeHolder = this.dialCodeHolderEl();
40127             
40128             this.trigger = this.el.select('div.flag-box',true).first();
40129             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40130             
40131             var _this = this;
40132             
40133             (function(){
40134                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40135                 _this.list.setWidth(lw);
40136             }).defer(100);
40137             
40138             this.list.on('mouseover', this.onViewOver, this);
40139             this.list.on('mousemove', this.onViewMove, this);
40140             this.inputEl().on("keyup", this.onKeyUp, this);
40141             
40142             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40143
40144             this.view = new Roo.View(this.list, this.tpl, {
40145                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40146             });
40147             
40148             this.view.on('click', this.onViewClick, this);
40149             this.setValue(this.defaultDialCode);
40150         },
40151         
40152         onTriggerClick : function(e)
40153         {
40154             Roo.log('trigger click');
40155             if(this.disabled){
40156                 return;
40157             }
40158             
40159             if(this.isExpanded()){
40160                 this.collapse();
40161                 this.hasFocus = false;
40162             }else {
40163                 this.store.load({});
40164                 this.hasFocus = true;
40165                 this.expand();
40166             }
40167         },
40168         
40169         isExpanded : function()
40170         {
40171             return this.list.isVisible();
40172         },
40173         
40174         collapse : function()
40175         {
40176             if(!this.isExpanded()){
40177                 return;
40178             }
40179             this.list.hide();
40180             Roo.get(document).un('mousedown', this.collapseIf, this);
40181             Roo.get(document).un('mousewheel', this.collapseIf, this);
40182             this.fireEvent('collapse', this);
40183             this.validate();
40184         },
40185         
40186         expand : function()
40187         {
40188             Roo.log('expand');
40189
40190             if(this.isExpanded() || !this.hasFocus){
40191                 return;
40192             }
40193             
40194             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40195             this.list.setWidth(lw);
40196             
40197             this.list.show();
40198             this.restrictHeight();
40199             
40200             Roo.get(document).on('mousedown', this.collapseIf, this);
40201             Roo.get(document).on('mousewheel', this.collapseIf, this);
40202             
40203             this.fireEvent('expand', this);
40204         },
40205         
40206         restrictHeight : function()
40207         {
40208             this.list.alignTo(this.inputEl(), this.listAlign);
40209             this.list.alignTo(this.inputEl(), this.listAlign);
40210         },
40211         
40212         onViewOver : function(e, t)
40213         {
40214             if(this.inKeyMode){
40215                 return;
40216             }
40217             var item = this.view.findItemFromChild(t);
40218             
40219             if(item){
40220                 var index = this.view.indexOf(item);
40221                 this.select(index, false);
40222             }
40223         },
40224
40225         // private
40226         onViewClick : function(view, doFocus, el, e)
40227         {
40228             var index = this.view.getSelectedIndexes()[0];
40229             
40230             var r = this.store.getAt(index);
40231             
40232             if(r){
40233                 this.onSelect(r, index);
40234             }
40235             if(doFocus !== false && !this.blockFocus){
40236                 this.inputEl().focus();
40237             }
40238         },
40239         
40240         onViewMove : function(e, t)
40241         {
40242             this.inKeyMode = false;
40243         },
40244         
40245         select : function(index, scrollIntoView)
40246         {
40247             this.selectedIndex = index;
40248             this.view.select(index);
40249             if(scrollIntoView !== false){
40250                 var el = this.view.getNode(index);
40251                 if(el){
40252                     this.list.scrollChildIntoView(el, false);
40253                 }
40254             }
40255         },
40256         
40257         createList : function()
40258         {
40259             this.list = Roo.get(document.body).createChild({
40260                 tag: 'ul',
40261                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40262                 style: 'display:none'
40263             });
40264             
40265             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40266         },
40267         
40268         collapseIf : function(e)
40269         {
40270             var in_combo  = e.within(this.el);
40271             var in_list =  e.within(this.list);
40272             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40273             
40274             if (in_combo || in_list || is_list) {
40275                 return;
40276             }
40277             this.collapse();
40278         },
40279         
40280         onSelect : function(record, index)
40281         {
40282             if(this.fireEvent('beforeselect', this, record, index) !== false){
40283                 
40284                 this.setFlagClass(record.data.iso2);
40285                 this.setDialCode(record.data.dialCode);
40286                 this.hasFocus = false;
40287                 this.collapse();
40288                 this.fireEvent('select', this, record, index);
40289             }
40290         },
40291         
40292         flagEl : function()
40293         {
40294             var flag = this.el.select('div.flag',true).first();
40295             if(!flag){
40296                 return false;
40297             }
40298             return flag;
40299         },
40300         
40301         dialCodeHolderEl : function()
40302         {
40303             var d = this.el.select('input.dial-code-holder',true).first();
40304             if(!d){
40305                 return false;
40306             }
40307             return d;
40308         },
40309         
40310         setDialCode : function(v)
40311         {
40312             this.dialCodeHolder.dom.value = '+'+v;
40313         },
40314         
40315         setFlagClass : function(n)
40316         {
40317             this.flag.dom.className = 'flag '+n;
40318         },
40319         
40320         getValue : function()
40321         {
40322             var v = this.inputEl().getValue();
40323             if(this.dialCodeHolder) {
40324                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40325             }
40326             return v;
40327         },
40328         
40329         setValue : function(v)
40330         {
40331             var d = this.getDialCode(v);
40332             
40333             //invalid dial code
40334             if(v.length == 0 || !d || d.length == 0) {
40335                 if(this.rendered){
40336                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40337                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40338                 }
40339                 return;
40340             }
40341             
40342             //valid dial code
40343             this.setFlagClass(this.dialCodeMapping[d].iso2);
40344             this.setDialCode(d);
40345             this.inputEl().dom.value = v.replace('+'+d,'');
40346             this.hiddenEl().dom.value = this.getValue();
40347             
40348             this.validate();
40349         },
40350         
40351         getDialCode : function(v)
40352         {
40353             v = v ||  '';
40354             
40355             if (v.length == 0) {
40356                 return this.dialCodeHolder.dom.value;
40357             }
40358             
40359             var dialCode = "";
40360             if (v.charAt(0) != "+") {
40361                 return false;
40362             }
40363             var numericChars = "";
40364             for (var i = 1; i < v.length; i++) {
40365               var c = v.charAt(i);
40366               if (!isNaN(c)) {
40367                 numericChars += c;
40368                 if (this.dialCodeMapping[numericChars]) {
40369                   dialCode = v.substr(1, i);
40370                 }
40371                 if (numericChars.length == 4) {
40372                   break;
40373                 }
40374               }
40375             }
40376             return dialCode;
40377         },
40378         
40379         reset : function()
40380         {
40381             this.setValue(this.defaultDialCode);
40382             this.validate();
40383         },
40384         
40385         hiddenEl : function()
40386         {
40387             return this.el.select('input.hidden-tel-input',true).first();
40388         },
40389         
40390         onKeyUp : function(e){
40391             
40392             var k = e.getKey();
40393             var c = e.getCharCode();
40394             
40395             if(
40396                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40397                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40398             ){
40399                 e.stopEvent();
40400             }
40401             
40402             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40403             //     return;
40404             // }
40405             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40406                 e.stopEvent();
40407             }
40408             
40409             this.setValue(this.getValue());
40410         }
40411         
40412 });
40413 /**
40414  * @class Roo.bootstrap.MoneyField
40415  * @extends Roo.bootstrap.ComboBox
40416  * Bootstrap MoneyField class
40417  * 
40418  * @constructor
40419  * Create a new MoneyField.
40420  * @param {Object} config Configuration options
40421  */
40422
40423 Roo.bootstrap.MoneyField = function(config) {
40424     
40425     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40426     
40427 };
40428
40429 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40430     
40431     /**
40432      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40433      */
40434     allowDecimals : true,
40435     /**
40436      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40437      */
40438     decimalSeparator : ".",
40439     /**
40440      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40441      */
40442     decimalPrecision : 0,
40443     /**
40444      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40445      */
40446     allowNegative : true,
40447     /**
40448      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40449      */
40450     allowZero: true,
40451     /**
40452      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40453      */
40454     minValue : Number.NEGATIVE_INFINITY,
40455     /**
40456      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40457      */
40458     maxValue : Number.MAX_VALUE,
40459     /**
40460      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40461      */
40462     minText : "The minimum value for this field is {0}",
40463     /**
40464      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40465      */
40466     maxText : "The maximum value for this field is {0}",
40467     /**
40468      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40469      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40470      */
40471     nanText : "{0} is not a valid number",
40472     /**
40473      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40474      */
40475     castInt : true,
40476     /**
40477      * @cfg {String} defaults currency of the MoneyField
40478      * value should be in lkey
40479      */
40480     defaultCurrency : false,
40481     /**
40482      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40483      */
40484     thousandsDelimiter : false,
40485     /**
40486      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40487      */
40488     max_length: false,
40489     
40490     inputlg : 9,
40491     inputmd : 9,
40492     inputsm : 9,
40493     inputxs : 6,
40494     
40495     store : false,
40496     
40497     getAutoCreate : function()
40498     {
40499         var align = this.labelAlign || this.parentLabelAlign();
40500         
40501         var id = Roo.id();
40502
40503         var cfg = {
40504             cls: 'form-group',
40505             cn: []
40506         };
40507
40508         var input =  {
40509             tag: 'input',
40510             id : id,
40511             cls : 'form-control roo-money-amount-input',
40512             autocomplete: 'new-password'
40513         };
40514         
40515         var hiddenInput = {
40516             tag: 'input',
40517             type: 'hidden',
40518             id: Roo.id(),
40519             cls: 'hidden-number-input'
40520         };
40521         
40522         if(this.max_length) {
40523             input.maxlength = this.max_length; 
40524         }
40525         
40526         if (this.name) {
40527             hiddenInput.name = this.name;
40528         }
40529
40530         if (this.disabled) {
40531             input.disabled = true;
40532         }
40533
40534         var clg = 12 - this.inputlg;
40535         var cmd = 12 - this.inputmd;
40536         var csm = 12 - this.inputsm;
40537         var cxs = 12 - this.inputxs;
40538         
40539         var container = {
40540             tag : 'div',
40541             cls : 'row roo-money-field',
40542             cn : [
40543                 {
40544                     tag : 'div',
40545                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40546                     cn : [
40547                         {
40548                             tag : 'div',
40549                             cls: 'roo-select2-container input-group',
40550                             cn: [
40551                                 {
40552                                     tag : 'input',
40553                                     cls : 'form-control roo-money-currency-input',
40554                                     autocomplete: 'new-password',
40555                                     readOnly : 1,
40556                                     name : this.currencyName
40557                                 },
40558                                 {
40559                                     tag :'span',
40560                                     cls : 'input-group-addon',
40561                                     cn : [
40562                                         {
40563                                             tag: 'span',
40564                                             cls: 'caret'
40565                                         }
40566                                     ]
40567                                 }
40568                             ]
40569                         }
40570                     ]
40571                 },
40572                 {
40573                     tag : 'div',
40574                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40575                     cn : [
40576                         {
40577                             tag: 'div',
40578                             cls: this.hasFeedback ? 'has-feedback' : '',
40579                             cn: [
40580                                 input
40581                             ]
40582                         }
40583                     ]
40584                 }
40585             ]
40586             
40587         };
40588         
40589         if (this.fieldLabel.length) {
40590             var indicator = {
40591                 tag: 'i',
40592                 tooltip: 'This field is required'
40593             };
40594
40595             var label = {
40596                 tag: 'label',
40597                 'for':  id,
40598                 cls: 'control-label',
40599                 cn: []
40600             };
40601
40602             var label_text = {
40603                 tag: 'span',
40604                 html: this.fieldLabel
40605             };
40606
40607             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40608             label.cn = [
40609                 indicator,
40610                 label_text
40611             ];
40612
40613             if(this.indicatorpos == 'right') {
40614                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40615                 label.cn = [
40616                     label_text,
40617                     indicator
40618                 ];
40619             }
40620
40621             if(align == 'left') {
40622                 container = {
40623                     tag: 'div',
40624                     cn: [
40625                         container
40626                     ]
40627                 };
40628
40629                 if(this.labelWidth > 12){
40630                     label.style = "width: " + this.labelWidth + 'px';
40631                 }
40632                 if(this.labelWidth < 13 && this.labelmd == 0){
40633                     this.labelmd = this.labelWidth;
40634                 }
40635                 if(this.labellg > 0){
40636                     label.cls += ' col-lg-' + this.labellg;
40637                     input.cls += ' col-lg-' + (12 - this.labellg);
40638                 }
40639                 if(this.labelmd > 0){
40640                     label.cls += ' col-md-' + this.labelmd;
40641                     container.cls += ' col-md-' + (12 - this.labelmd);
40642                 }
40643                 if(this.labelsm > 0){
40644                     label.cls += ' col-sm-' + this.labelsm;
40645                     container.cls += ' col-sm-' + (12 - this.labelsm);
40646                 }
40647                 if(this.labelxs > 0){
40648                     label.cls += ' col-xs-' + this.labelxs;
40649                     container.cls += ' col-xs-' + (12 - this.labelxs);
40650                 }
40651             }
40652         }
40653
40654         cfg.cn = [
40655             label,
40656             container,
40657             hiddenInput
40658         ];
40659         
40660         var settings = this;
40661
40662         ['xs','sm','md','lg'].map(function(size){
40663             if (settings[size]) {
40664                 cfg.cls += ' col-' + size + '-' + settings[size];
40665             }
40666         });
40667         
40668         return cfg;
40669     },
40670     
40671     initEvents : function()
40672     {
40673         this.indicator = this.indicatorEl();
40674         
40675         this.initCurrencyEvent();
40676         
40677         this.initNumberEvent();
40678     },
40679     
40680     initCurrencyEvent : function()
40681     {
40682         if (!this.store) {
40683             throw "can not find store for combo";
40684         }
40685         
40686         this.store = Roo.factory(this.store, Roo.data);
40687         this.store.parent = this;
40688         
40689         this.createList();
40690         
40691         this.triggerEl = this.el.select('.input-group-addon', true).first();
40692         
40693         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40694         
40695         var _this = this;
40696         
40697         (function(){
40698             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40699             _this.list.setWidth(lw);
40700         }).defer(100);
40701         
40702         this.list.on('mouseover', this.onViewOver, this);
40703         this.list.on('mousemove', this.onViewMove, this);
40704         this.list.on('scroll', this.onViewScroll, this);
40705         
40706         if(!this.tpl){
40707             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40708         }
40709         
40710         this.view = new Roo.View(this.list, this.tpl, {
40711             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40712         });
40713         
40714         this.view.on('click', this.onViewClick, this);
40715         
40716         this.store.on('beforeload', this.onBeforeLoad, this);
40717         this.store.on('load', this.onLoad, this);
40718         this.store.on('loadexception', this.onLoadException, this);
40719         
40720         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40721             "up" : function(e){
40722                 this.inKeyMode = true;
40723                 this.selectPrev();
40724             },
40725
40726             "down" : function(e){
40727                 if(!this.isExpanded()){
40728                     this.onTriggerClick();
40729                 }else{
40730                     this.inKeyMode = true;
40731                     this.selectNext();
40732                 }
40733             },
40734
40735             "enter" : function(e){
40736                 this.collapse();
40737                 
40738                 if(this.fireEvent("specialkey", this, e)){
40739                     this.onViewClick(false);
40740                 }
40741                 
40742                 return true;
40743             },
40744
40745             "esc" : function(e){
40746                 this.collapse();
40747             },
40748
40749             "tab" : function(e){
40750                 this.collapse();
40751                 
40752                 if(this.fireEvent("specialkey", this, e)){
40753                     this.onViewClick(false);
40754                 }
40755                 
40756                 return true;
40757             },
40758
40759             scope : this,
40760
40761             doRelay : function(foo, bar, hname){
40762                 if(hname == 'down' || this.scope.isExpanded()){
40763                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40764                 }
40765                 return true;
40766             },
40767
40768             forceKeyDown: true
40769         });
40770         
40771         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40772         
40773     },
40774     
40775     initNumberEvent : function(e)
40776     {
40777         this.inputEl().on("keydown" , this.fireKey,  this);
40778         this.inputEl().on("focus", this.onFocus,  this);
40779         this.inputEl().on("blur", this.onBlur,  this);
40780         
40781         this.inputEl().relayEvent('keyup', this);
40782         
40783         if(this.indicator){
40784             this.indicator.addClass('invisible');
40785         }
40786  
40787         this.originalValue = this.getValue();
40788         
40789         if(this.validationEvent == 'keyup'){
40790             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40791             this.inputEl().on('keyup', this.filterValidation, this);
40792         }
40793         else if(this.validationEvent !== false){
40794             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40795         }
40796         
40797         if(this.selectOnFocus){
40798             this.on("focus", this.preFocus, this);
40799             
40800         }
40801         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40802             this.inputEl().on("keypress", this.filterKeys, this);
40803         } else {
40804             this.inputEl().relayEvent('keypress', this);
40805         }
40806         
40807         var allowed = "0123456789";
40808         
40809         if(this.allowDecimals){
40810             allowed += this.decimalSeparator;
40811         }
40812         
40813         if(this.allowNegative){
40814             allowed += "-";
40815         }
40816         
40817         if(this.thousandsDelimiter) {
40818             allowed += ",";
40819         }
40820         
40821         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40822         
40823         var keyPress = function(e){
40824             
40825             var k = e.getKey();
40826             
40827             var c = e.getCharCode();
40828             
40829             if(
40830                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40831                     allowed.indexOf(String.fromCharCode(c)) === -1
40832             ){
40833                 e.stopEvent();
40834                 return;
40835             }
40836             
40837             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40838                 return;
40839             }
40840             
40841             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40842                 e.stopEvent();
40843             }
40844         };
40845         
40846         this.inputEl().on("keypress", keyPress, this);
40847         
40848     },
40849     
40850     onTriggerClick : function(e)
40851     {   
40852         if(this.disabled){
40853             return;
40854         }
40855         
40856         this.page = 0;
40857         this.loadNext = false;
40858         
40859         if(this.isExpanded()){
40860             this.collapse();
40861             return;
40862         }
40863         
40864         this.hasFocus = true;
40865         
40866         if(this.triggerAction == 'all') {
40867             this.doQuery(this.allQuery, true);
40868             return;
40869         }
40870         
40871         this.doQuery(this.getRawValue());
40872     },
40873     
40874     getCurrency : function()
40875     {   
40876         var v = this.currencyEl().getValue();
40877         
40878         return v;
40879     },
40880     
40881     restrictHeight : function()
40882     {
40883         this.list.alignTo(this.currencyEl(), this.listAlign);
40884         this.list.alignTo(this.currencyEl(), this.listAlign);
40885     },
40886     
40887     onViewClick : function(view, doFocus, el, e)
40888     {
40889         var index = this.view.getSelectedIndexes()[0];
40890         
40891         var r = this.store.getAt(index);
40892         
40893         if(r){
40894             this.onSelect(r, index);
40895         }
40896     },
40897     
40898     onSelect : function(record, index){
40899         
40900         if(this.fireEvent('beforeselect', this, record, index) !== false){
40901         
40902             this.setFromCurrencyData(index > -1 ? record.data : false);
40903             
40904             this.collapse();
40905             
40906             this.fireEvent('select', this, record, index);
40907         }
40908     },
40909     
40910     setFromCurrencyData : function(o)
40911     {
40912         var currency = '';
40913         
40914         this.lastCurrency = o;
40915         
40916         if (this.currencyField) {
40917             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40918         } else {
40919             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40920         }
40921         
40922         this.lastSelectionText = currency;
40923         
40924         //setting default currency
40925         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40926             this.setCurrency(this.defaultCurrency);
40927             return;
40928         }
40929         
40930         this.setCurrency(currency);
40931     },
40932     
40933     setFromData : function(o)
40934     {
40935         var c = {};
40936         
40937         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40938         
40939         this.setFromCurrencyData(c);
40940         
40941         var value = '';
40942         
40943         if (this.name) {
40944             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40945         } else {
40946             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40947         }
40948         
40949         this.setValue(value);
40950         
40951     },
40952     
40953     setCurrency : function(v)
40954     {   
40955         this.currencyValue = v;
40956         
40957         if(this.rendered){
40958             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40959             this.validate();
40960         }
40961     },
40962     
40963     setValue : function(v)
40964     {
40965         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40966         
40967         this.value = v;
40968         
40969         if(this.rendered){
40970             
40971             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40972             
40973             this.inputEl().dom.value = (v == '') ? '' :
40974                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40975             
40976             if(!this.allowZero && v === '0') {
40977                 this.hiddenEl().dom.value = '';
40978                 this.inputEl().dom.value = '';
40979             }
40980             
40981             this.validate();
40982         }
40983     },
40984     
40985     getRawValue : function()
40986     {
40987         var v = this.inputEl().getValue();
40988         
40989         return v;
40990     },
40991     
40992     getValue : function()
40993     {
40994         return this.fixPrecision(this.parseValue(this.getRawValue()));
40995     },
40996     
40997     parseValue : function(value)
40998     {
40999         if(this.thousandsDelimiter) {
41000             value += "";
41001             r = new RegExp(",", "g");
41002             value = value.replace(r, "");
41003         }
41004         
41005         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41006         return isNaN(value) ? '' : value;
41007         
41008     },
41009     
41010     fixPrecision : function(value)
41011     {
41012         if(this.thousandsDelimiter) {
41013             value += "";
41014             r = new RegExp(",", "g");
41015             value = value.replace(r, "");
41016         }
41017         
41018         var nan = isNaN(value);
41019         
41020         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41021             return nan ? '' : value;
41022         }
41023         return parseFloat(value).toFixed(this.decimalPrecision);
41024     },
41025     
41026     decimalPrecisionFcn : function(v)
41027     {
41028         return Math.floor(v);
41029     },
41030     
41031     validateValue : function(value)
41032     {
41033         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41034             return false;
41035         }
41036         
41037         var num = this.parseValue(value);
41038         
41039         if(isNaN(num)){
41040             this.markInvalid(String.format(this.nanText, value));
41041             return false;
41042         }
41043         
41044         if(num < this.minValue){
41045             this.markInvalid(String.format(this.minText, this.minValue));
41046             return false;
41047         }
41048         
41049         if(num > this.maxValue){
41050             this.markInvalid(String.format(this.maxText, this.maxValue));
41051             return false;
41052         }
41053         
41054         return true;
41055     },
41056     
41057     validate : function()
41058     {
41059         if(this.disabled || this.allowBlank){
41060             this.markValid();
41061             return true;
41062         }
41063         
41064         var currency = this.getCurrency();
41065         
41066         if(this.validateValue(this.getRawValue()) && currency.length){
41067             this.markValid();
41068             return true;
41069         }
41070         
41071         this.markInvalid();
41072         return false;
41073     },
41074     
41075     getName: function()
41076     {
41077         return this.name;
41078     },
41079     
41080     beforeBlur : function()
41081     {
41082         if(!this.castInt){
41083             return;
41084         }
41085         
41086         var v = this.parseValue(this.getRawValue());
41087         
41088         if(v || v == 0){
41089             this.setValue(v);
41090         }
41091     },
41092     
41093     onBlur : function()
41094     {
41095         this.beforeBlur();
41096         
41097         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41098             //this.el.removeClass(this.focusClass);
41099         }
41100         
41101         this.hasFocus = false;
41102         
41103         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41104             this.validate();
41105         }
41106         
41107         var v = this.getValue();
41108         
41109         if(String(v) !== String(this.startValue)){
41110             this.fireEvent('change', this, v, this.startValue);
41111         }
41112         
41113         this.fireEvent("blur", this);
41114     },
41115     
41116     inputEl : function()
41117     {
41118         return this.el.select('.roo-money-amount-input', true).first();
41119     },
41120     
41121     currencyEl : function()
41122     {
41123         return this.el.select('.roo-money-currency-input', true).first();
41124     },
41125     
41126     hiddenEl : function()
41127     {
41128         return this.el.select('input.hidden-number-input',true).first();
41129     }
41130     
41131 });