sync
[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             h_row[0].classList.replace(
7135                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7136                 "col-"+size_cls[0]+"-"+size_cls[1]
7137             );
7138             
7139             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7140         }
7141         
7142         for(var i = 0; i < rows.length; i++) {
7143             
7144             for(var j = 0; j < w.length; j++) {
7145                 
7146                 var size_cls = w[j].split("-");
7147                 
7148                 if(!Number.isInteger(size_cls[1] * 1)) {
7149                     continue;
7150                 }
7151                 
7152                 if(!this.colModel.config[col_index][size_cls[0]]) {
7153                     continue;
7154                 }
7155                 
7156                 rows[i].classList.replace(
7157                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7158                     "col-"+size_cls[0]+"-"+size_cls[1]
7159                 );
7160             }
7161         }
7162     }
7163 });
7164
7165  
7166
7167  /*
7168  * - LGPL
7169  *
7170  * table cell
7171  * 
7172  */
7173
7174 /**
7175  * @class Roo.bootstrap.TableCell
7176  * @extends Roo.bootstrap.Component
7177  * Bootstrap TableCell class
7178  * @cfg {String} html cell contain text
7179  * @cfg {String} cls cell class
7180  * @cfg {String} tag cell tag (td|th) default td
7181  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7182  * @cfg {String} align Aligns the content in a cell
7183  * @cfg {String} axis Categorizes cells
7184  * @cfg {String} bgcolor Specifies the background color of a cell
7185  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7186  * @cfg {Number} colspan Specifies the number of columns a cell should span
7187  * @cfg {String} headers Specifies one or more header cells a cell is related to
7188  * @cfg {Number} height Sets the height of a cell
7189  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7190  * @cfg {Number} rowspan Sets the number of rows a cell should span
7191  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7192  * @cfg {String} valign Vertical aligns the content in a cell
7193  * @cfg {Number} width Specifies the width of a cell
7194  * 
7195  * @constructor
7196  * Create a new TableCell
7197  * @param {Object} config The config object
7198  */
7199
7200 Roo.bootstrap.TableCell = function(config){
7201     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7202 };
7203
7204 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7205     
7206     html: false,
7207     cls: false,
7208     tag: false,
7209     abbr: false,
7210     align: false,
7211     axis: false,
7212     bgcolor: false,
7213     charoff: false,
7214     colspan: false,
7215     headers: false,
7216     height: false,
7217     nowrap: false,
7218     rowspan: false,
7219     scope: false,
7220     valign: false,
7221     width: false,
7222     
7223     
7224     getAutoCreate : function(){
7225         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7226         
7227         cfg = {
7228             tag: 'td'
7229         };
7230         
7231         if(this.tag){
7232             cfg.tag = this.tag;
7233         }
7234         
7235         if (this.html) {
7236             cfg.html=this.html
7237         }
7238         if (this.cls) {
7239             cfg.cls=this.cls
7240         }
7241         if (this.abbr) {
7242             cfg.abbr=this.abbr
7243         }
7244         if (this.align) {
7245             cfg.align=this.align
7246         }
7247         if (this.axis) {
7248             cfg.axis=this.axis
7249         }
7250         if (this.bgcolor) {
7251             cfg.bgcolor=this.bgcolor
7252         }
7253         if (this.charoff) {
7254             cfg.charoff=this.charoff
7255         }
7256         if (this.colspan) {
7257             cfg.colspan=this.colspan
7258         }
7259         if (this.headers) {
7260             cfg.headers=this.headers
7261         }
7262         if (this.height) {
7263             cfg.height=this.height
7264         }
7265         if (this.nowrap) {
7266             cfg.nowrap=this.nowrap
7267         }
7268         if (this.rowspan) {
7269             cfg.rowspan=this.rowspan
7270         }
7271         if (this.scope) {
7272             cfg.scope=this.scope
7273         }
7274         if (this.valign) {
7275             cfg.valign=this.valign
7276         }
7277         if (this.width) {
7278             cfg.width=this.width
7279         }
7280         
7281         
7282         return cfg;
7283     }
7284    
7285 });
7286
7287  
7288
7289  /*
7290  * - LGPL
7291  *
7292  * table row
7293  * 
7294  */
7295
7296 /**
7297  * @class Roo.bootstrap.TableRow
7298  * @extends Roo.bootstrap.Component
7299  * Bootstrap TableRow class
7300  * @cfg {String} cls row class
7301  * @cfg {String} align Aligns the content in a table row
7302  * @cfg {String} bgcolor Specifies a background color for a table row
7303  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7304  * @cfg {String} valign Vertical aligns the content in a table row
7305  * 
7306  * @constructor
7307  * Create a new TableRow
7308  * @param {Object} config The config object
7309  */
7310
7311 Roo.bootstrap.TableRow = function(config){
7312     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7313 };
7314
7315 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7316     
7317     cls: false,
7318     align: false,
7319     bgcolor: false,
7320     charoff: false,
7321     valign: false,
7322     
7323     getAutoCreate : function(){
7324         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7325         
7326         cfg = {
7327             tag: 'tr'
7328         };
7329             
7330         if(this.cls){
7331             cfg.cls = this.cls;
7332         }
7333         if(this.align){
7334             cfg.align = this.align;
7335         }
7336         if(this.bgcolor){
7337             cfg.bgcolor = this.bgcolor;
7338         }
7339         if(this.charoff){
7340             cfg.charoff = this.charoff;
7341         }
7342         if(this.valign){
7343             cfg.valign = this.valign;
7344         }
7345         
7346         return cfg;
7347     }
7348    
7349 });
7350
7351  
7352
7353  /*
7354  * - LGPL
7355  *
7356  * table body
7357  * 
7358  */
7359
7360 /**
7361  * @class Roo.bootstrap.TableBody
7362  * @extends Roo.bootstrap.Component
7363  * Bootstrap TableBody class
7364  * @cfg {String} cls element class
7365  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7366  * @cfg {String} align Aligns the content inside the element
7367  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7368  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7369  * 
7370  * @constructor
7371  * Create a new TableBody
7372  * @param {Object} config The config object
7373  */
7374
7375 Roo.bootstrap.TableBody = function(config){
7376     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7377 };
7378
7379 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7380     
7381     cls: false,
7382     tag: false,
7383     align: false,
7384     charoff: false,
7385     valign: false,
7386     
7387     getAutoCreate : function(){
7388         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7389         
7390         cfg = {
7391             tag: 'tbody'
7392         };
7393             
7394         if (this.cls) {
7395             cfg.cls=this.cls
7396         }
7397         if(this.tag){
7398             cfg.tag = this.tag;
7399         }
7400         
7401         if(this.align){
7402             cfg.align = this.align;
7403         }
7404         if(this.charoff){
7405             cfg.charoff = this.charoff;
7406         }
7407         if(this.valign){
7408             cfg.valign = this.valign;
7409         }
7410         
7411         return cfg;
7412     }
7413     
7414     
7415 //    initEvents : function()
7416 //    {
7417 //        
7418 //        if(!this.store){
7419 //            return;
7420 //        }
7421 //        
7422 //        this.store = Roo.factory(this.store, Roo.data);
7423 //        this.store.on('load', this.onLoad, this);
7424 //        
7425 //        this.store.load();
7426 //        
7427 //    },
7428 //    
7429 //    onLoad: function () 
7430 //    {   
7431 //        this.fireEvent('load', this);
7432 //    }
7433 //    
7434 //   
7435 });
7436
7437  
7438
7439  /*
7440  * Based on:
7441  * Ext JS Library 1.1.1
7442  * Copyright(c) 2006-2007, Ext JS, LLC.
7443  *
7444  * Originally Released Under LGPL - original licence link has changed is not relivant.
7445  *
7446  * Fork - LGPL
7447  * <script type="text/javascript">
7448  */
7449
7450 // as we use this in bootstrap.
7451 Roo.namespace('Roo.form');
7452  /**
7453  * @class Roo.form.Action
7454  * Internal Class used to handle form actions
7455  * @constructor
7456  * @param {Roo.form.BasicForm} el The form element or its id
7457  * @param {Object} config Configuration options
7458  */
7459
7460  
7461  
7462 // define the action interface
7463 Roo.form.Action = function(form, options){
7464     this.form = form;
7465     this.options = options || {};
7466 };
7467 /**
7468  * Client Validation Failed
7469  * @const 
7470  */
7471 Roo.form.Action.CLIENT_INVALID = 'client';
7472 /**
7473  * Server Validation Failed
7474  * @const 
7475  */
7476 Roo.form.Action.SERVER_INVALID = 'server';
7477  /**
7478  * Connect to Server Failed
7479  * @const 
7480  */
7481 Roo.form.Action.CONNECT_FAILURE = 'connect';
7482 /**
7483  * Reading Data from Server Failed
7484  * @const 
7485  */
7486 Roo.form.Action.LOAD_FAILURE = 'load';
7487
7488 Roo.form.Action.prototype = {
7489     type : 'default',
7490     failureType : undefined,
7491     response : undefined,
7492     result : undefined,
7493
7494     // interface method
7495     run : function(options){
7496
7497     },
7498
7499     // interface method
7500     success : function(response){
7501
7502     },
7503
7504     // interface method
7505     handleResponse : function(response){
7506
7507     },
7508
7509     // default connection failure
7510     failure : function(response){
7511         
7512         this.response = response;
7513         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7514         this.form.afterAction(this, false);
7515     },
7516
7517     processResponse : function(response){
7518         this.response = response;
7519         if(!response.responseText){
7520             return true;
7521         }
7522         this.result = this.handleResponse(response);
7523         return this.result;
7524     },
7525
7526     // utility functions used internally
7527     getUrl : function(appendParams){
7528         var url = this.options.url || this.form.url || this.form.el.dom.action;
7529         if(appendParams){
7530             var p = this.getParams();
7531             if(p){
7532                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7533             }
7534         }
7535         return url;
7536     },
7537
7538     getMethod : function(){
7539         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7540     },
7541
7542     getParams : function(){
7543         var bp = this.form.baseParams;
7544         var p = this.options.params;
7545         if(p){
7546             if(typeof p == "object"){
7547                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7548             }else if(typeof p == 'string' && bp){
7549                 p += '&' + Roo.urlEncode(bp);
7550             }
7551         }else if(bp){
7552             p = Roo.urlEncode(bp);
7553         }
7554         return p;
7555     },
7556
7557     createCallback : function(){
7558         return {
7559             success: this.success,
7560             failure: this.failure,
7561             scope: this,
7562             timeout: (this.form.timeout*1000),
7563             upload: this.form.fileUpload ? this.success : undefined
7564         };
7565     }
7566 };
7567
7568 Roo.form.Action.Submit = function(form, options){
7569     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7570 };
7571
7572 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7573     type : 'submit',
7574
7575     haveProgress : false,
7576     uploadComplete : false,
7577     
7578     // uploadProgress indicator.
7579     uploadProgress : function()
7580     {
7581         if (!this.form.progressUrl) {
7582             return;
7583         }
7584         
7585         if (!this.haveProgress) {
7586             Roo.MessageBox.progress("Uploading", "Uploading");
7587         }
7588         if (this.uploadComplete) {
7589            Roo.MessageBox.hide();
7590            return;
7591         }
7592         
7593         this.haveProgress = true;
7594    
7595         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7596         
7597         var c = new Roo.data.Connection();
7598         c.request({
7599             url : this.form.progressUrl,
7600             params: {
7601                 id : uid
7602             },
7603             method: 'GET',
7604             success : function(req){
7605                //console.log(data);
7606                 var rdata = false;
7607                 var edata;
7608                 try  {
7609                    rdata = Roo.decode(req.responseText)
7610                 } catch (e) {
7611                     Roo.log("Invalid data from server..");
7612                     Roo.log(edata);
7613                     return;
7614                 }
7615                 if (!rdata || !rdata.success) {
7616                     Roo.log(rdata);
7617                     Roo.MessageBox.alert(Roo.encode(rdata));
7618                     return;
7619                 }
7620                 var data = rdata.data;
7621                 
7622                 if (this.uploadComplete) {
7623                    Roo.MessageBox.hide();
7624                    return;
7625                 }
7626                    
7627                 if (data){
7628                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7629                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7630                     );
7631                 }
7632                 this.uploadProgress.defer(2000,this);
7633             },
7634        
7635             failure: function(data) {
7636                 Roo.log('progress url failed ');
7637                 Roo.log(data);
7638             },
7639             scope : this
7640         });
7641            
7642     },
7643     
7644     
7645     run : function()
7646     {
7647         // run get Values on the form, so it syncs any secondary forms.
7648         this.form.getValues();
7649         
7650         var o = this.options;
7651         var method = this.getMethod();
7652         var isPost = method == 'POST';
7653         if(o.clientValidation === false || this.form.isValid()){
7654             
7655             if (this.form.progressUrl) {
7656                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7657                     (new Date() * 1) + '' + Math.random());
7658                     
7659             } 
7660             
7661             
7662             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7663                 form:this.form.el.dom,
7664                 url:this.getUrl(!isPost),
7665                 method: method,
7666                 params:isPost ? this.getParams() : null,
7667                 isUpload: this.form.fileUpload
7668             }));
7669             
7670             this.uploadProgress();
7671
7672         }else if (o.clientValidation !== false){ // client validation failed
7673             this.failureType = Roo.form.Action.CLIENT_INVALID;
7674             this.form.afterAction(this, false);
7675         }
7676     },
7677
7678     success : function(response)
7679     {
7680         this.uploadComplete= true;
7681         if (this.haveProgress) {
7682             Roo.MessageBox.hide();
7683         }
7684         
7685         
7686         var result = this.processResponse(response);
7687         if(result === true || result.success){
7688             this.form.afterAction(this, true);
7689             return;
7690         }
7691         if(result.errors){
7692             this.form.markInvalid(result.errors);
7693             this.failureType = Roo.form.Action.SERVER_INVALID;
7694         }
7695         this.form.afterAction(this, false);
7696     },
7697     failure : function(response)
7698     {
7699         this.uploadComplete= true;
7700         if (this.haveProgress) {
7701             Roo.MessageBox.hide();
7702         }
7703         
7704         this.response = response;
7705         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7706         this.form.afterAction(this, false);
7707     },
7708     
7709     handleResponse : function(response){
7710         if(this.form.errorReader){
7711             var rs = this.form.errorReader.read(response);
7712             var errors = [];
7713             if(rs.records){
7714                 for(var i = 0, len = rs.records.length; i < len; i++) {
7715                     var r = rs.records[i];
7716                     errors[i] = r.data;
7717                 }
7718             }
7719             if(errors.length < 1){
7720                 errors = null;
7721             }
7722             return {
7723                 success : rs.success,
7724                 errors : errors
7725             };
7726         }
7727         var ret = false;
7728         try {
7729             ret = Roo.decode(response.responseText);
7730         } catch (e) {
7731             ret = {
7732                 success: false,
7733                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7734                 errors : []
7735             };
7736         }
7737         return ret;
7738         
7739     }
7740 });
7741
7742
7743 Roo.form.Action.Load = function(form, options){
7744     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7745     this.reader = this.form.reader;
7746 };
7747
7748 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7749     type : 'load',
7750
7751     run : function(){
7752         
7753         Roo.Ajax.request(Roo.apply(
7754                 this.createCallback(), {
7755                     method:this.getMethod(),
7756                     url:this.getUrl(false),
7757                     params:this.getParams()
7758         }));
7759     },
7760
7761     success : function(response){
7762         
7763         var result = this.processResponse(response);
7764         if(result === true || !result.success || !result.data){
7765             this.failureType = Roo.form.Action.LOAD_FAILURE;
7766             this.form.afterAction(this, false);
7767             return;
7768         }
7769         this.form.clearInvalid();
7770         this.form.setValues(result.data);
7771         this.form.afterAction(this, true);
7772     },
7773
7774     handleResponse : function(response){
7775         if(this.form.reader){
7776             var rs = this.form.reader.read(response);
7777             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7778             return {
7779                 success : rs.success,
7780                 data : data
7781             };
7782         }
7783         return Roo.decode(response.responseText);
7784     }
7785 });
7786
7787 Roo.form.Action.ACTION_TYPES = {
7788     'load' : Roo.form.Action.Load,
7789     'submit' : Roo.form.Action.Submit
7790 };/*
7791  * - LGPL
7792  *
7793  * form
7794  *
7795  */
7796
7797 /**
7798  * @class Roo.bootstrap.Form
7799  * @extends Roo.bootstrap.Component
7800  * Bootstrap Form class
7801  * @cfg {String} method  GET | POST (default POST)
7802  * @cfg {String} labelAlign top | left (default top)
7803  * @cfg {String} align left  | right - for navbars
7804  * @cfg {Boolean} loadMask load mask when submit (default true)
7805
7806  *
7807  * @constructor
7808  * Create a new Form
7809  * @param {Object} config The config object
7810  */
7811
7812
7813 Roo.bootstrap.Form = function(config){
7814     
7815     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7816     
7817     Roo.bootstrap.Form.popover.apply();
7818     
7819     this.addEvents({
7820         /**
7821          * @event clientvalidation
7822          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7823          * @param {Form} this
7824          * @param {Boolean} valid true if the form has passed client-side validation
7825          */
7826         clientvalidation: true,
7827         /**
7828          * @event beforeaction
7829          * Fires before any action is performed. Return false to cancel the action.
7830          * @param {Form} this
7831          * @param {Action} action The action to be performed
7832          */
7833         beforeaction: true,
7834         /**
7835          * @event actionfailed
7836          * Fires when an action fails.
7837          * @param {Form} this
7838          * @param {Action} action The action that failed
7839          */
7840         actionfailed : true,
7841         /**
7842          * @event actioncomplete
7843          * Fires when an action is completed.
7844          * @param {Form} this
7845          * @param {Action} action The action that completed
7846          */
7847         actioncomplete : true
7848     });
7849 };
7850
7851 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7852
7853      /**
7854      * @cfg {String} method
7855      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7856      */
7857     method : 'POST',
7858     /**
7859      * @cfg {String} url
7860      * The URL to use for form actions if one isn't supplied in the action options.
7861      */
7862     /**
7863      * @cfg {Boolean} fileUpload
7864      * Set to true if this form is a file upload.
7865      */
7866
7867     /**
7868      * @cfg {Object} baseParams
7869      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7870      */
7871
7872     /**
7873      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7874      */
7875     timeout: 30,
7876     /**
7877      * @cfg {Sting} align (left|right) for navbar forms
7878      */
7879     align : 'left',
7880
7881     // private
7882     activeAction : null,
7883
7884     /**
7885      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7886      * element by passing it or its id or mask the form itself by passing in true.
7887      * @type Mixed
7888      */
7889     waitMsgTarget : false,
7890
7891     loadMask : true,
7892     
7893     /**
7894      * @cfg {Boolean} errorMask (true|false) default false
7895      */
7896     errorMask : false,
7897     
7898     /**
7899      * @cfg {Number} maskOffset Default 100
7900      */
7901     maskOffset : 100,
7902     
7903     /**
7904      * @cfg {Boolean} maskBody
7905      */
7906     maskBody : false,
7907
7908     getAutoCreate : function(){
7909
7910         var cfg = {
7911             tag: 'form',
7912             method : this.method || 'POST',
7913             id : this.id || Roo.id(),
7914             cls : ''
7915         };
7916         if (this.parent().xtype.match(/^Nav/)) {
7917             cfg.cls = 'navbar-form navbar-' + this.align;
7918
7919         }
7920
7921         if (this.labelAlign == 'left' ) {
7922             cfg.cls += ' form-horizontal';
7923         }
7924
7925
7926         return cfg;
7927     },
7928     initEvents : function()
7929     {
7930         this.el.on('submit', this.onSubmit, this);
7931         // this was added as random key presses on the form where triggering form submit.
7932         this.el.on('keypress', function(e) {
7933             if (e.getCharCode() != 13) {
7934                 return true;
7935             }
7936             // we might need to allow it for textareas.. and some other items.
7937             // check e.getTarget().
7938
7939             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7940                 return true;
7941             }
7942
7943             Roo.log("keypress blocked");
7944
7945             e.preventDefault();
7946             return false;
7947         });
7948         
7949     },
7950     // private
7951     onSubmit : function(e){
7952         e.stopEvent();
7953     },
7954
7955      /**
7956      * Returns true if client-side validation on the form is successful.
7957      * @return Boolean
7958      */
7959     isValid : function(){
7960         var items = this.getItems();
7961         var valid = true;
7962         var target = false;
7963         
7964         items.each(function(f){
7965             
7966             if(f.validate()){
7967                 return;
7968             }
7969             
7970             Roo.log('invalid field: ' + f.name);
7971             
7972             valid = false;
7973
7974             if(!target && f.el.isVisible(true)){
7975                 target = f;
7976             }
7977            
7978         });
7979         
7980         if(this.errorMask && !valid){
7981             Roo.bootstrap.Form.popover.mask(this, target);
7982         }
7983         
7984         return valid;
7985     },
7986     
7987     /**
7988      * Returns true if any fields in this form have changed since their original load.
7989      * @return Boolean
7990      */
7991     isDirty : function(){
7992         var dirty = false;
7993         var items = this.getItems();
7994         items.each(function(f){
7995            if(f.isDirty()){
7996                dirty = true;
7997                return false;
7998            }
7999            return true;
8000         });
8001         return dirty;
8002     },
8003      /**
8004      * Performs a predefined action (submit or load) or custom actions you define on this form.
8005      * @param {String} actionName The name of the action type
8006      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8007      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8008      * accept other config options):
8009      * <pre>
8010 Property          Type             Description
8011 ----------------  ---------------  ----------------------------------------------------------------------------------
8012 url               String           The url for the action (defaults to the form's url)
8013 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8014 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8015 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8016                                    validate the form on the client (defaults to false)
8017      * </pre>
8018      * @return {BasicForm} this
8019      */
8020     doAction : function(action, options){
8021         if(typeof action == 'string'){
8022             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8023         }
8024         if(this.fireEvent('beforeaction', this, action) !== false){
8025             this.beforeAction(action);
8026             action.run.defer(100, action);
8027         }
8028         return this;
8029     },
8030
8031     // private
8032     beforeAction : function(action){
8033         var o = action.options;
8034         
8035         if(this.loadMask){
8036             
8037             if(this.maskBody){
8038                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8039             } else {
8040                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8041             }
8042         }
8043         // not really supported yet.. ??
8044
8045         //if(this.waitMsgTarget === true){
8046         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8047         //}else if(this.waitMsgTarget){
8048         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8049         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8050         //}else {
8051         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8052        // }
8053
8054     },
8055
8056     // private
8057     afterAction : function(action, success){
8058         this.activeAction = null;
8059         var o = action.options;
8060
8061         if(this.loadMask){
8062             
8063             if(this.maskBody){
8064                 Roo.get(document.body).unmask();
8065             } else {
8066                 this.el.unmask();
8067             }
8068         }
8069         
8070         //if(this.waitMsgTarget === true){
8071 //            this.el.unmask();
8072         //}else if(this.waitMsgTarget){
8073         //    this.waitMsgTarget.unmask();
8074         //}else{
8075         //    Roo.MessageBox.updateProgress(1);
8076         //    Roo.MessageBox.hide();
8077        // }
8078         //
8079         if(success){
8080             if(o.reset){
8081                 this.reset();
8082             }
8083             Roo.callback(o.success, o.scope, [this, action]);
8084             this.fireEvent('actioncomplete', this, action);
8085
8086         }else{
8087
8088             // failure condition..
8089             // we have a scenario where updates need confirming.
8090             // eg. if a locking scenario exists..
8091             // we look for { errors : { needs_confirm : true }} in the response.
8092             if (
8093                 (typeof(action.result) != 'undefined')  &&
8094                 (typeof(action.result.errors) != 'undefined')  &&
8095                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8096            ){
8097                 var _t = this;
8098                 Roo.log("not supported yet");
8099                  /*
8100
8101                 Roo.MessageBox.confirm(
8102                     "Change requires confirmation",
8103                     action.result.errorMsg,
8104                     function(r) {
8105                         if (r != 'yes') {
8106                             return;
8107                         }
8108                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8109                     }
8110
8111                 );
8112                 */
8113
8114
8115                 return;
8116             }
8117
8118             Roo.callback(o.failure, o.scope, [this, action]);
8119             // show an error message if no failed handler is set..
8120             if (!this.hasListener('actionfailed')) {
8121                 Roo.log("need to add dialog support");
8122                 /*
8123                 Roo.MessageBox.alert("Error",
8124                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8125                         action.result.errorMsg :
8126                         "Saving Failed, please check your entries or try again"
8127                 );
8128                 */
8129             }
8130
8131             this.fireEvent('actionfailed', this, action);
8132         }
8133
8134     },
8135     /**
8136      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8137      * @param {String} id The value to search for
8138      * @return Field
8139      */
8140     findField : function(id){
8141         var items = this.getItems();
8142         var field = items.get(id);
8143         if(!field){
8144              items.each(function(f){
8145                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8146                     field = f;
8147                     return false;
8148                 }
8149                 return true;
8150             });
8151         }
8152         return field || null;
8153     },
8154      /**
8155      * Mark fields in this form invalid in bulk.
8156      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8157      * @return {BasicForm} this
8158      */
8159     markInvalid : function(errors){
8160         if(errors instanceof Array){
8161             for(var i = 0, len = errors.length; i < len; i++){
8162                 var fieldError = errors[i];
8163                 var f = this.findField(fieldError.id);
8164                 if(f){
8165                     f.markInvalid(fieldError.msg);
8166                 }
8167             }
8168         }else{
8169             var field, id;
8170             for(id in errors){
8171                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8172                     field.markInvalid(errors[id]);
8173                 }
8174             }
8175         }
8176         //Roo.each(this.childForms || [], function (f) {
8177         //    f.markInvalid(errors);
8178         //});
8179
8180         return this;
8181     },
8182
8183     /**
8184      * Set values for fields in this form in bulk.
8185      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8186      * @return {BasicForm} this
8187      */
8188     setValues : function(values){
8189         if(values instanceof Array){ // array of objects
8190             for(var i = 0, len = values.length; i < len; i++){
8191                 var v = values[i];
8192                 var f = this.findField(v.id);
8193                 if(f){
8194                     f.setValue(v.value);
8195                     if(this.trackResetOnLoad){
8196                         f.originalValue = f.getValue();
8197                     }
8198                 }
8199             }
8200         }else{ // object hash
8201             var field, id;
8202             for(id in values){
8203                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8204
8205                     if (field.setFromData &&
8206                         field.valueField &&
8207                         field.displayField &&
8208                         // combos' with local stores can
8209                         // be queried via setValue()
8210                         // to set their value..
8211                         (field.store && !field.store.isLocal)
8212                         ) {
8213                         // it's a combo
8214                         var sd = { };
8215                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8216                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8217                         field.setFromData(sd);
8218
8219                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8220                         
8221                         field.setFromData(values);
8222                         
8223                     } else {
8224                         field.setValue(values[id]);
8225                     }
8226
8227
8228                     if(this.trackResetOnLoad){
8229                         field.originalValue = field.getValue();
8230                     }
8231                 }
8232             }
8233         }
8234
8235         //Roo.each(this.childForms || [], function (f) {
8236         //    f.setValues(values);
8237         //});
8238
8239         return this;
8240     },
8241
8242     /**
8243      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8244      * they are returned as an array.
8245      * @param {Boolean} asString
8246      * @return {Object}
8247      */
8248     getValues : function(asString){
8249         //if (this.childForms) {
8250             // copy values from the child forms
8251         //    Roo.each(this.childForms, function (f) {
8252         //        this.setValues(f.getValues());
8253         //    }, this);
8254         //}
8255
8256
8257
8258         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8259         if(asString === true){
8260             return fs;
8261         }
8262         return Roo.urlDecode(fs);
8263     },
8264
8265     /**
8266      * Returns the fields in this form as an object with key/value pairs.
8267      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8268      * @return {Object}
8269      */
8270     getFieldValues : function(with_hidden)
8271     {
8272         var items = this.getItems();
8273         var ret = {};
8274         items.each(function(f){
8275             
8276             if (!f.getName()) {
8277                 return;
8278             }
8279             
8280             var v = f.getValue();
8281             
8282             if (f.inputType =='radio') {
8283                 if (typeof(ret[f.getName()]) == 'undefined') {
8284                     ret[f.getName()] = ''; // empty..
8285                 }
8286
8287                 if (!f.el.dom.checked) {
8288                     return;
8289
8290                 }
8291                 v = f.el.dom.value;
8292
8293             }
8294             
8295             if(f.xtype == 'MoneyField'){
8296                 ret[f.currencyName] = f.getCurrency();
8297             }
8298
8299             // not sure if this supported any more..
8300             if ((typeof(v) == 'object') && f.getRawValue) {
8301                 v = f.getRawValue() ; // dates..
8302             }
8303             // combo boxes where name != hiddenName...
8304             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8305                 ret[f.name] = f.getRawValue();
8306             }
8307             ret[f.getName()] = v;
8308         });
8309
8310         return ret;
8311     },
8312
8313     /**
8314      * Clears all invalid messages in this form.
8315      * @return {BasicForm} this
8316      */
8317     clearInvalid : function(){
8318         var items = this.getItems();
8319
8320         items.each(function(f){
8321            f.clearInvalid();
8322         });
8323
8324         return this;
8325     },
8326
8327     /**
8328      * Resets this form.
8329      * @return {BasicForm} this
8330      */
8331     reset : function(){
8332         var items = this.getItems();
8333         items.each(function(f){
8334             f.reset();
8335         });
8336
8337         Roo.each(this.childForms || [], function (f) {
8338             f.reset();
8339         });
8340
8341
8342         return this;
8343     },
8344     
8345     getItems : function()
8346     {
8347         var r=new Roo.util.MixedCollection(false, function(o){
8348             return o.id || (o.id = Roo.id());
8349         });
8350         var iter = function(el) {
8351             if (el.inputEl) {
8352                 r.add(el);
8353             }
8354             if (!el.items) {
8355                 return;
8356             }
8357             Roo.each(el.items,function(e) {
8358                 iter(e);
8359             });
8360         };
8361
8362         iter(this);
8363         return r;
8364     },
8365     
8366     hideFields : function(items)
8367     {
8368         Roo.each(items, function(i){
8369             
8370             var f = this.findField(i);
8371             
8372             if(!f){
8373                 return;
8374             }
8375             
8376             f.hide();
8377             
8378         }, this);
8379     },
8380     
8381     showFields : function(items)
8382     {
8383         Roo.each(items, function(i){
8384             
8385             var f = this.findField(i);
8386             
8387             if(!f){
8388                 return;
8389             }
8390             
8391             f.show();
8392             
8393         }, this);
8394     }
8395
8396 });
8397
8398 Roo.apply(Roo.bootstrap.Form, {
8399     
8400     popover : {
8401         
8402         padding : 5,
8403         
8404         isApplied : false,
8405         
8406         isMasked : false,
8407         
8408         form : false,
8409         
8410         target : false,
8411         
8412         toolTip : false,
8413         
8414         intervalID : false,
8415         
8416         maskEl : false,
8417         
8418         apply : function()
8419         {
8420             if(this.isApplied){
8421                 return;
8422             }
8423             
8424             this.maskEl = {
8425                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8426                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8427                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8428                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8429             };
8430             
8431             this.maskEl.top.enableDisplayMode("block");
8432             this.maskEl.left.enableDisplayMode("block");
8433             this.maskEl.bottom.enableDisplayMode("block");
8434             this.maskEl.right.enableDisplayMode("block");
8435             
8436             this.toolTip = new Roo.bootstrap.Tooltip({
8437                 cls : 'roo-form-error-popover',
8438                 alignment : {
8439                     'left' : ['r-l', [-2,0], 'right'],
8440                     'right' : ['l-r', [2,0], 'left'],
8441                     'bottom' : ['tl-bl', [0,2], 'top'],
8442                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8443                 }
8444             });
8445             
8446             this.toolTip.render(Roo.get(document.body));
8447
8448             this.toolTip.el.enableDisplayMode("block");
8449             
8450             Roo.get(document.body).on('click', function(){
8451                 this.unmask();
8452             }, this);
8453             
8454             Roo.get(document.body).on('touchstart', function(){
8455                 this.unmask();
8456             }, this);
8457             
8458             this.isApplied = true
8459         },
8460         
8461         mask : function(form, target)
8462         {
8463             this.form = form;
8464             
8465             this.target = target;
8466             
8467             if(!this.form.errorMask || !target.el){
8468                 return;
8469             }
8470             
8471             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8472             
8473             Roo.log(scrollable);
8474             
8475             var ot = this.target.el.calcOffsetsTo(scrollable);
8476             
8477             var scrollTo = ot[1] - this.form.maskOffset;
8478             
8479             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8480             
8481             scrollable.scrollTo('top', scrollTo);
8482             
8483             var box = this.target.el.getBox();
8484             Roo.log(box);
8485             var zIndex = Roo.bootstrap.Modal.zIndex++;
8486
8487             
8488             this.maskEl.top.setStyle('position', 'absolute');
8489             this.maskEl.top.setStyle('z-index', zIndex);
8490             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8491             this.maskEl.top.setLeft(0);
8492             this.maskEl.top.setTop(0);
8493             this.maskEl.top.show();
8494             
8495             this.maskEl.left.setStyle('position', 'absolute');
8496             this.maskEl.left.setStyle('z-index', zIndex);
8497             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8498             this.maskEl.left.setLeft(0);
8499             this.maskEl.left.setTop(box.y - this.padding);
8500             this.maskEl.left.show();
8501
8502             this.maskEl.bottom.setStyle('position', 'absolute');
8503             this.maskEl.bottom.setStyle('z-index', zIndex);
8504             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8505             this.maskEl.bottom.setLeft(0);
8506             this.maskEl.bottom.setTop(box.bottom + this.padding);
8507             this.maskEl.bottom.show();
8508
8509             this.maskEl.right.setStyle('position', 'absolute');
8510             this.maskEl.right.setStyle('z-index', zIndex);
8511             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8512             this.maskEl.right.setLeft(box.right + this.padding);
8513             this.maskEl.right.setTop(box.y - this.padding);
8514             this.maskEl.right.show();
8515
8516             this.toolTip.bindEl = this.target.el;
8517
8518             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8519
8520             var tip = this.target.blankText;
8521
8522             if(this.target.getValue() !== '' ) {
8523                 
8524                 if (this.target.invalidText.length) {
8525                     tip = this.target.invalidText;
8526                 } else if (this.target.regexText.length){
8527                     tip = this.target.regexText;
8528                 }
8529             }
8530
8531             this.toolTip.show(tip);
8532
8533             this.intervalID = window.setInterval(function() {
8534                 Roo.bootstrap.Form.popover.unmask();
8535             }, 10000);
8536
8537             window.onwheel = function(){ return false;};
8538             
8539             (function(){ this.isMasked = true; }).defer(500, this);
8540             
8541         },
8542         
8543         unmask : function()
8544         {
8545             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8546                 return;
8547             }
8548             
8549             this.maskEl.top.setStyle('position', 'absolute');
8550             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8551             this.maskEl.top.hide();
8552
8553             this.maskEl.left.setStyle('position', 'absolute');
8554             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8555             this.maskEl.left.hide();
8556
8557             this.maskEl.bottom.setStyle('position', 'absolute');
8558             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8559             this.maskEl.bottom.hide();
8560
8561             this.maskEl.right.setStyle('position', 'absolute');
8562             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8563             this.maskEl.right.hide();
8564             
8565             this.toolTip.hide();
8566             
8567             this.toolTip.el.hide();
8568             
8569             window.onwheel = function(){ return true;};
8570             
8571             if(this.intervalID){
8572                 window.clearInterval(this.intervalID);
8573                 this.intervalID = false;
8574             }
8575             
8576             this.isMasked = false;
8577             
8578         }
8579         
8580     }
8581     
8582 });
8583
8584 /*
8585  * Based on:
8586  * Ext JS Library 1.1.1
8587  * Copyright(c) 2006-2007, Ext JS, LLC.
8588  *
8589  * Originally Released Under LGPL - original licence link has changed is not relivant.
8590  *
8591  * Fork - LGPL
8592  * <script type="text/javascript">
8593  */
8594 /**
8595  * @class Roo.form.VTypes
8596  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8597  * @singleton
8598  */
8599 Roo.form.VTypes = function(){
8600     // closure these in so they are only created once.
8601     var alpha = /^[a-zA-Z_]+$/;
8602     var alphanum = /^[a-zA-Z0-9_]+$/;
8603     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8604     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8605
8606     // All these messages and functions are configurable
8607     return {
8608         /**
8609          * The function used to validate email addresses
8610          * @param {String} value The email address
8611          */
8612         'email' : function(v){
8613             return email.test(v);
8614         },
8615         /**
8616          * The error text to display when the email validation function returns false
8617          * @type String
8618          */
8619         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8620         /**
8621          * The keystroke filter mask to be applied on email input
8622          * @type RegExp
8623          */
8624         'emailMask' : /[a-z0-9_\.\-@]/i,
8625
8626         /**
8627          * The function used to validate URLs
8628          * @param {String} value The URL
8629          */
8630         'url' : function(v){
8631             return url.test(v);
8632         },
8633         /**
8634          * The error text to display when the url validation function returns false
8635          * @type String
8636          */
8637         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8638         
8639         /**
8640          * The function used to validate alpha values
8641          * @param {String} value The value
8642          */
8643         'alpha' : function(v){
8644             return alpha.test(v);
8645         },
8646         /**
8647          * The error text to display when the alpha validation function returns false
8648          * @type String
8649          */
8650         'alphaText' : 'This field should only contain letters and _',
8651         /**
8652          * The keystroke filter mask to be applied on alpha input
8653          * @type RegExp
8654          */
8655         'alphaMask' : /[a-z_]/i,
8656
8657         /**
8658          * The function used to validate alphanumeric values
8659          * @param {String} value The value
8660          */
8661         'alphanum' : function(v){
8662             return alphanum.test(v);
8663         },
8664         /**
8665          * The error text to display when the alphanumeric validation function returns false
8666          * @type String
8667          */
8668         'alphanumText' : 'This field should only contain letters, numbers and _',
8669         /**
8670          * The keystroke filter mask to be applied on alphanumeric input
8671          * @type RegExp
8672          */
8673         'alphanumMask' : /[a-z0-9_]/i
8674     };
8675 }();/*
8676  * - LGPL
8677  *
8678  * Input
8679  * 
8680  */
8681
8682 /**
8683  * @class Roo.bootstrap.Input
8684  * @extends Roo.bootstrap.Component
8685  * Bootstrap Input class
8686  * @cfg {Boolean} disabled is it disabled
8687  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8688  * @cfg {String} name name of the input
8689  * @cfg {string} fieldLabel - the label associated
8690  * @cfg {string} placeholder - placeholder to put in text.
8691  * @cfg {string}  before - input group add on before
8692  * @cfg {string} after - input group add on after
8693  * @cfg {string} size - (lg|sm) or leave empty..
8694  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8695  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8696  * @cfg {Number} md colspan out of 12 for computer-sized screens
8697  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8698  * @cfg {string} value default value of the input
8699  * @cfg {Number} labelWidth set the width of label 
8700  * @cfg {Number} labellg set the width of label (1-12)
8701  * @cfg {Number} labelmd set the width of label (1-12)
8702  * @cfg {Number} labelsm set the width of label (1-12)
8703  * @cfg {Number} labelxs set the width of label (1-12)
8704  * @cfg {String} labelAlign (top|left)
8705  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8706  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8707  * @cfg {String} indicatorpos (left|right) default left
8708  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8709  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8710
8711  * @cfg {String} align (left|center|right) Default left
8712  * @cfg {Boolean} forceFeedback (true|false) Default false
8713  * 
8714  * @constructor
8715  * Create a new Input
8716  * @param {Object} config The config object
8717  */
8718
8719 Roo.bootstrap.Input = function(config){
8720     
8721     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8722     
8723     this.addEvents({
8724         /**
8725          * @event focus
8726          * Fires when this field receives input focus.
8727          * @param {Roo.form.Field} this
8728          */
8729         focus : true,
8730         /**
8731          * @event blur
8732          * Fires when this field loses input focus.
8733          * @param {Roo.form.Field} this
8734          */
8735         blur : true,
8736         /**
8737          * @event specialkey
8738          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8739          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8740          * @param {Roo.form.Field} this
8741          * @param {Roo.EventObject} e The event object
8742          */
8743         specialkey : true,
8744         /**
8745          * @event change
8746          * Fires just before the field blurs if the field value has changed.
8747          * @param {Roo.form.Field} this
8748          * @param {Mixed} newValue The new value
8749          * @param {Mixed} oldValue The original value
8750          */
8751         change : true,
8752         /**
8753          * @event invalid
8754          * Fires after the field has been marked as invalid.
8755          * @param {Roo.form.Field} this
8756          * @param {String} msg The validation message
8757          */
8758         invalid : true,
8759         /**
8760          * @event valid
8761          * Fires after the field has been validated with no errors.
8762          * @param {Roo.form.Field} this
8763          */
8764         valid : true,
8765          /**
8766          * @event keyup
8767          * Fires after the key up
8768          * @param {Roo.form.Field} this
8769          * @param {Roo.EventObject}  e The event Object
8770          */
8771         keyup : true
8772     });
8773 };
8774
8775 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8776      /**
8777      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8778       automatic validation (defaults to "keyup").
8779      */
8780     validationEvent : "keyup",
8781      /**
8782      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8783      */
8784     validateOnBlur : true,
8785     /**
8786      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8787      */
8788     validationDelay : 250,
8789      /**
8790      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8791      */
8792     focusClass : "x-form-focus",  // not needed???
8793     
8794        
8795     /**
8796      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8797      */
8798     invalidClass : "has-warning",
8799     
8800     /**
8801      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8802      */
8803     validClass : "has-success",
8804     
8805     /**
8806      * @cfg {Boolean} hasFeedback (true|false) default true
8807      */
8808     hasFeedback : true,
8809     
8810     /**
8811      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8812      */
8813     invalidFeedbackClass : "glyphicon-warning-sign",
8814     
8815     /**
8816      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8817      */
8818     validFeedbackClass : "glyphicon-ok",
8819     
8820     /**
8821      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8822      */
8823     selectOnFocus : false,
8824     
8825      /**
8826      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8827      */
8828     maskRe : null,
8829        /**
8830      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8831      */
8832     vtype : null,
8833     
8834       /**
8835      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8836      */
8837     disableKeyFilter : false,
8838     
8839        /**
8840      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8841      */
8842     disabled : false,
8843      /**
8844      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8845      */
8846     allowBlank : true,
8847     /**
8848      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8849      */
8850     blankText : "Please complete this mandatory field",
8851     
8852      /**
8853      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8854      */
8855     minLength : 0,
8856     /**
8857      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8858      */
8859     maxLength : Number.MAX_VALUE,
8860     /**
8861      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8862      */
8863     minLengthText : "The minimum length for this field is {0}",
8864     /**
8865      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8866      */
8867     maxLengthText : "The maximum length for this field is {0}",
8868   
8869     
8870     /**
8871      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8872      * If available, this function will be called only after the basic validators all return true, and will be passed the
8873      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8874      */
8875     validator : null,
8876     /**
8877      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8878      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8879      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8880      */
8881     regex : null,
8882     /**
8883      * @cfg {String} regexText -- Depricated - use Invalid Text
8884      */
8885     regexText : "",
8886     
8887     /**
8888      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8889      */
8890     invalidText : "",
8891     
8892     
8893     
8894     autocomplete: false,
8895     
8896     
8897     fieldLabel : '',
8898     inputType : 'text',
8899     
8900     name : false,
8901     placeholder: false,
8902     before : false,
8903     after : false,
8904     size : false,
8905     hasFocus : false,
8906     preventMark: false,
8907     isFormField : true,
8908     value : '',
8909     labelWidth : 2,
8910     labelAlign : false,
8911     readOnly : false,
8912     align : false,
8913     formatedValue : false,
8914     forceFeedback : false,
8915     
8916     indicatorpos : 'left',
8917     
8918     labellg : 0,
8919     labelmd : 0,
8920     labelsm : 0,
8921     labelxs : 0,
8922     
8923     capture : '',
8924     accept : '',
8925     
8926     parentLabelAlign : function()
8927     {
8928         var parent = this;
8929         while (parent.parent()) {
8930             parent = parent.parent();
8931             if (typeof(parent.labelAlign) !='undefined') {
8932                 return parent.labelAlign;
8933             }
8934         }
8935         return 'left';
8936         
8937     },
8938     
8939     getAutoCreate : function()
8940     {
8941         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8942         
8943         var id = Roo.id();
8944         
8945         var cfg = {};
8946         
8947         if(this.inputType != 'hidden'){
8948             cfg.cls = 'form-group' //input-group
8949         }
8950         
8951         var input =  {
8952             tag: 'input',
8953             id : id,
8954             type : this.inputType,
8955             value : this.value,
8956             cls : 'form-control',
8957             placeholder : this.placeholder || '',
8958             autocomplete : this.autocomplete || 'new-password'
8959         };
8960         
8961         if(this.capture.length){
8962             input.capture = this.capture;
8963         }
8964         
8965         if(this.accept.length){
8966             input.accept = this.accept + "/*";
8967         }
8968         
8969         if(this.align){
8970             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8971         }
8972         
8973         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8974             input.maxLength = this.maxLength;
8975         }
8976         
8977         if (this.disabled) {
8978             input.disabled=true;
8979         }
8980         
8981         if (this.readOnly) {
8982             input.readonly=true;
8983         }
8984         
8985         if (this.name) {
8986             input.name = this.name;
8987         }
8988         
8989         if (this.size) {
8990             input.cls += ' input-' + this.size;
8991         }
8992         
8993         var settings=this;
8994         ['xs','sm','md','lg'].map(function(size){
8995             if (settings[size]) {
8996                 cfg.cls += ' col-' + size + '-' + settings[size];
8997             }
8998         });
8999         
9000         var inputblock = input;
9001         
9002         var feedback = {
9003             tag: 'span',
9004             cls: 'glyphicon form-control-feedback'
9005         };
9006             
9007         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9008             
9009             inputblock = {
9010                 cls : 'has-feedback',
9011                 cn :  [
9012                     input,
9013                     feedback
9014                 ] 
9015             };  
9016         }
9017         
9018         if (this.before || this.after) {
9019             
9020             inputblock = {
9021                 cls : 'input-group',
9022                 cn :  [] 
9023             };
9024             
9025             if (this.before && typeof(this.before) == 'string') {
9026                 
9027                 inputblock.cn.push({
9028                     tag :'span',
9029                     cls : 'roo-input-before input-group-addon',
9030                     html : this.before
9031                 });
9032             }
9033             if (this.before && typeof(this.before) == 'object') {
9034                 this.before = Roo.factory(this.before);
9035                 
9036                 inputblock.cn.push({
9037                     tag :'span',
9038                     cls : 'roo-input-before input-group-' +
9039                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9040                 });
9041             }
9042             
9043             inputblock.cn.push(input);
9044             
9045             if (this.after && typeof(this.after) == 'string') {
9046                 inputblock.cn.push({
9047                     tag :'span',
9048                     cls : 'roo-input-after input-group-addon',
9049                     html : this.after
9050                 });
9051             }
9052             if (this.after && typeof(this.after) == 'object') {
9053                 this.after = Roo.factory(this.after);
9054                 
9055                 inputblock.cn.push({
9056                     tag :'span',
9057                     cls : 'roo-input-after input-group-' +
9058                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9059                 });
9060             }
9061             
9062             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9063                 inputblock.cls += ' has-feedback';
9064                 inputblock.cn.push(feedback);
9065             }
9066         };
9067         
9068         if (align ==='left' && this.fieldLabel.length) {
9069             
9070             cfg.cls += ' roo-form-group-label-left';
9071             
9072             cfg.cn = [
9073                 {
9074                     tag : 'i',
9075                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9076                     tooltip : 'This field is required'
9077                 },
9078                 {
9079                     tag: 'label',
9080                     'for' :  id,
9081                     cls : 'control-label',
9082                     html : this.fieldLabel
9083
9084                 },
9085                 {
9086                     cls : "", 
9087                     cn: [
9088                         inputblock
9089                     ]
9090                 }
9091             ];
9092             
9093             var labelCfg = cfg.cn[1];
9094             var contentCfg = cfg.cn[2];
9095             
9096             if(this.indicatorpos == 'right'){
9097                 cfg.cn = [
9098                     {
9099                         tag: 'label',
9100                         'for' :  id,
9101                         cls : 'control-label',
9102                         cn : [
9103                             {
9104                                 tag : 'span',
9105                                 html : this.fieldLabel
9106                             },
9107                             {
9108                                 tag : 'i',
9109                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9110                                 tooltip : 'This field is required'
9111                             }
9112                         ]
9113                     },
9114                     {
9115                         cls : "",
9116                         cn: [
9117                             inputblock
9118                         ]
9119                     }
9120
9121                 ];
9122                 
9123                 labelCfg = cfg.cn[0];
9124                 contentCfg = cfg.cn[1];
9125             
9126             }
9127             
9128             if(this.labelWidth > 12){
9129                 labelCfg.style = "width: " + this.labelWidth + 'px';
9130             }
9131             
9132             if(this.labelWidth < 13 && this.labelmd == 0){
9133                 this.labelmd = this.labelWidth;
9134             }
9135             
9136             if(this.labellg > 0){
9137                 labelCfg.cls += ' col-lg-' + this.labellg;
9138                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9139             }
9140             
9141             if(this.labelmd > 0){
9142                 labelCfg.cls += ' col-md-' + this.labelmd;
9143                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9144             }
9145             
9146             if(this.labelsm > 0){
9147                 labelCfg.cls += ' col-sm-' + this.labelsm;
9148                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9149             }
9150             
9151             if(this.labelxs > 0){
9152                 labelCfg.cls += ' col-xs-' + this.labelxs;
9153                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9154             }
9155             
9156             
9157         } else if ( this.fieldLabel.length) {
9158                 
9159             cfg.cn = [
9160                 {
9161                     tag : 'i',
9162                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9163                     tooltip : 'This field is required'
9164                 },
9165                 {
9166                     tag: 'label',
9167                    //cls : 'input-group-addon',
9168                     html : this.fieldLabel
9169
9170                 },
9171
9172                inputblock
9173
9174            ];
9175            
9176            if(this.indicatorpos == 'right'){
9177                 
9178                 cfg.cn = [
9179                     {
9180                         tag: 'label',
9181                        //cls : 'input-group-addon',
9182                         html : this.fieldLabel
9183
9184                     },
9185                     {
9186                         tag : 'i',
9187                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9188                         tooltip : 'This field is required'
9189                     },
9190
9191                    inputblock
9192
9193                ];
9194
9195             }
9196
9197         } else {
9198             
9199             cfg.cn = [
9200
9201                     inputblock
9202
9203             ];
9204                 
9205                 
9206         };
9207         
9208         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9209            cfg.cls += ' navbar-form';
9210         }
9211         
9212         if (this.parentType === 'NavGroup') {
9213            cfg.cls += ' navbar-form';
9214            cfg.tag = 'li';
9215         }
9216         
9217         return cfg;
9218         
9219     },
9220     /**
9221      * return the real input element.
9222      */
9223     inputEl: function ()
9224     {
9225         return this.el.select('input.form-control',true).first();
9226     },
9227     
9228     tooltipEl : function()
9229     {
9230         return this.inputEl();
9231     },
9232     
9233     indicatorEl : function()
9234     {
9235         var indicator = this.el.select('i.roo-required-indicator',true).first();
9236         
9237         if(!indicator){
9238             return false;
9239         }
9240         
9241         return indicator;
9242         
9243     },
9244     
9245     setDisabled : function(v)
9246     {
9247         var i  = this.inputEl().dom;
9248         if (!v) {
9249             i.removeAttribute('disabled');
9250             return;
9251             
9252         }
9253         i.setAttribute('disabled','true');
9254     },
9255     initEvents : function()
9256     {
9257           
9258         this.inputEl().on("keydown" , this.fireKey,  this);
9259         this.inputEl().on("focus", this.onFocus,  this);
9260         this.inputEl().on("blur", this.onBlur,  this);
9261         
9262         this.inputEl().relayEvent('keyup', this);
9263         
9264         this.indicator = this.indicatorEl();
9265         
9266         if(this.indicator){
9267             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9268         }
9269  
9270         // reference to original value for reset
9271         this.originalValue = this.getValue();
9272         //Roo.form.TextField.superclass.initEvents.call(this);
9273         if(this.validationEvent == 'keyup'){
9274             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9275             this.inputEl().on('keyup', this.filterValidation, this);
9276         }
9277         else if(this.validationEvent !== false){
9278             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9279         }
9280         
9281         if(this.selectOnFocus){
9282             this.on("focus", this.preFocus, this);
9283             
9284         }
9285         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9286             this.inputEl().on("keypress", this.filterKeys, this);
9287         } else {
9288             this.inputEl().relayEvent('keypress', this);
9289         }
9290        /* if(this.grow){
9291             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9292             this.el.on("click", this.autoSize,  this);
9293         }
9294         */
9295         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9296             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9297         }
9298         
9299         if (typeof(this.before) == 'object') {
9300             this.before.render(this.el.select('.roo-input-before',true).first());
9301         }
9302         if (typeof(this.after) == 'object') {
9303             this.after.render(this.el.select('.roo-input-after',true).first());
9304         }
9305         
9306         this.inputEl().on('change', this.onChange, this);
9307         
9308     },
9309     filterValidation : function(e){
9310         if(!e.isNavKeyPress()){
9311             this.validationTask.delay(this.validationDelay);
9312         }
9313     },
9314      /**
9315      * Validates the field value
9316      * @return {Boolean} True if the value is valid, else false
9317      */
9318     validate : function(){
9319         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9320         if(this.disabled || this.validateValue(this.getRawValue())){
9321             this.markValid();
9322             return true;
9323         }
9324         
9325         this.markInvalid();
9326         return false;
9327     },
9328     
9329     
9330     /**
9331      * Validates a value according to the field's validation rules and marks the field as invalid
9332      * if the validation fails
9333      * @param {Mixed} value The value to validate
9334      * @return {Boolean} True if the value is valid, else false
9335      */
9336     validateValue : function(value)
9337     {
9338         if(this.getVisibilityEl().hasClass('hidden')){
9339             return true;
9340         }
9341         
9342         if(value.length < 1)  { // if it's blank
9343             if(this.allowBlank){
9344                 return true;
9345             }
9346             return false;
9347         }
9348         
9349         if(value.length < this.minLength){
9350             return false;
9351         }
9352         if(value.length > this.maxLength){
9353             return false;
9354         }
9355         if(this.vtype){
9356             var vt = Roo.form.VTypes;
9357             if(!vt[this.vtype](value, this)){
9358                 return false;
9359             }
9360         }
9361         if(typeof this.validator == "function"){
9362             var msg = this.validator(value);
9363             if(msg !== true){
9364                 return false;
9365             }
9366             if (typeof(msg) == 'string') {
9367                 this.invalidText = msg;
9368             }
9369         }
9370         
9371         if(this.regex && !this.regex.test(value)){
9372             return false;
9373         }
9374         
9375         return true;
9376     },
9377     
9378      // private
9379     fireKey : function(e){
9380         //Roo.log('field ' + e.getKey());
9381         if(e.isNavKeyPress()){
9382             this.fireEvent("specialkey", this, e);
9383         }
9384     },
9385     focus : function (selectText){
9386         if(this.rendered){
9387             this.inputEl().focus();
9388             if(selectText === true){
9389                 this.inputEl().dom.select();
9390             }
9391         }
9392         return this;
9393     } ,
9394     
9395     onFocus : function(){
9396         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9397            // this.el.addClass(this.focusClass);
9398         }
9399         if(!this.hasFocus){
9400             this.hasFocus = true;
9401             this.startValue = this.getValue();
9402             this.fireEvent("focus", this);
9403         }
9404     },
9405     
9406     beforeBlur : Roo.emptyFn,
9407
9408     
9409     // private
9410     onBlur : function(){
9411         this.beforeBlur();
9412         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9413             //this.el.removeClass(this.focusClass);
9414         }
9415         this.hasFocus = false;
9416         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9417             this.validate();
9418         }
9419         var v = this.getValue();
9420         if(String(v) !== String(this.startValue)){
9421             this.fireEvent('change', this, v, this.startValue);
9422         }
9423         this.fireEvent("blur", this);
9424     },
9425     
9426     onChange : function(e)
9427     {
9428         var v = this.getValue();
9429         if(String(v) !== String(this.startValue)){
9430             this.fireEvent('change', this, v, this.startValue);
9431         }
9432         
9433     },
9434     
9435     /**
9436      * Resets the current field value to the originally loaded value and clears any validation messages
9437      */
9438     reset : function(){
9439         this.setValue(this.originalValue);
9440         this.validate();
9441     },
9442      /**
9443      * Returns the name of the field
9444      * @return {Mixed} name The name field
9445      */
9446     getName: function(){
9447         return this.name;
9448     },
9449      /**
9450      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9451      * @return {Mixed} value The field value
9452      */
9453     getValue : function(){
9454         
9455         var v = this.inputEl().getValue();
9456         
9457         return v;
9458     },
9459     /**
9460      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9461      * @return {Mixed} value The field value
9462      */
9463     getRawValue : function(){
9464         var v = this.inputEl().getValue();
9465         
9466         return v;
9467     },
9468     
9469     /**
9470      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9471      * @param {Mixed} value The value to set
9472      */
9473     setRawValue : function(v){
9474         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9475     },
9476     
9477     selectText : function(start, end){
9478         var v = this.getRawValue();
9479         if(v.length > 0){
9480             start = start === undefined ? 0 : start;
9481             end = end === undefined ? v.length : end;
9482             var d = this.inputEl().dom;
9483             if(d.setSelectionRange){
9484                 d.setSelectionRange(start, end);
9485             }else if(d.createTextRange){
9486                 var range = d.createTextRange();
9487                 range.moveStart("character", start);
9488                 range.moveEnd("character", v.length-end);
9489                 range.select();
9490             }
9491         }
9492     },
9493     
9494     /**
9495      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9496      * @param {Mixed} value The value to set
9497      */
9498     setValue : function(v){
9499         this.value = v;
9500         if(this.rendered){
9501             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9502             this.validate();
9503         }
9504     },
9505     
9506     /*
9507     processValue : function(value){
9508         if(this.stripCharsRe){
9509             var newValue = value.replace(this.stripCharsRe, '');
9510             if(newValue !== value){
9511                 this.setRawValue(newValue);
9512                 return newValue;
9513             }
9514         }
9515         return value;
9516     },
9517   */
9518     preFocus : function(){
9519         
9520         if(this.selectOnFocus){
9521             this.inputEl().dom.select();
9522         }
9523     },
9524     filterKeys : function(e){
9525         var k = e.getKey();
9526         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9527             return;
9528         }
9529         var c = e.getCharCode(), cc = String.fromCharCode(c);
9530         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9531             return;
9532         }
9533         if(!this.maskRe.test(cc)){
9534             e.stopEvent();
9535         }
9536     },
9537      /**
9538      * Clear any invalid styles/messages for this field
9539      */
9540     clearInvalid : function(){
9541         
9542         if(!this.el || this.preventMark){ // not rendered
9543             return;
9544         }
9545         
9546      
9547         this.el.removeClass(this.invalidClass);
9548         
9549         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9550             
9551             var feedback = this.el.select('.form-control-feedback', true).first();
9552             
9553             if(feedback){
9554                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9555             }
9556             
9557         }
9558         
9559         if(this.indicator){
9560             this.indicator.removeClass('visible');
9561             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9562         }
9563         
9564         this.fireEvent('valid', this);
9565     },
9566     
9567      /**
9568      * Mark this field as valid
9569      */
9570     markValid : function()
9571     {
9572         if(!this.el  || this.preventMark){ // not rendered...
9573             return;
9574         }
9575         
9576         this.el.removeClass([this.invalidClass, this.validClass]);
9577         
9578         var feedback = this.el.select('.form-control-feedback', true).first();
9579             
9580         if(feedback){
9581             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9582         }
9583         
9584         if(this.indicator){
9585             this.indicator.removeClass('visible');
9586             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9587         }
9588         
9589         if(this.disabled){
9590             return;
9591         }
9592         
9593         if(this.allowBlank && !this.getRawValue().length){
9594             return;
9595         }
9596         
9597         this.el.addClass(this.validClass);
9598         
9599         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9600             
9601             var feedback = this.el.select('.form-control-feedback', true).first();
9602             
9603             if(feedback){
9604                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9605                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9606             }
9607             
9608         }
9609         
9610         this.fireEvent('valid', this);
9611     },
9612     
9613      /**
9614      * Mark this field as invalid
9615      * @param {String} msg The validation message
9616      */
9617     markInvalid : function(msg)
9618     {
9619         if(!this.el  || this.preventMark){ // not rendered
9620             return;
9621         }
9622         
9623         this.el.removeClass([this.invalidClass, this.validClass]);
9624         
9625         var feedback = this.el.select('.form-control-feedback', true).first();
9626             
9627         if(feedback){
9628             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9629         }
9630
9631         if(this.disabled){
9632             return;
9633         }
9634         
9635         if(this.allowBlank && !this.getRawValue().length){
9636             return;
9637         }
9638         
9639         if(this.indicator){
9640             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9641             this.indicator.addClass('visible');
9642         }
9643         
9644         this.el.addClass(this.invalidClass);
9645         
9646         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9647             
9648             var feedback = this.el.select('.form-control-feedback', true).first();
9649             
9650             if(feedback){
9651                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9652                 
9653                 if(this.getValue().length || this.forceFeedback){
9654                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9655                 }
9656                 
9657             }
9658             
9659         }
9660         
9661         this.fireEvent('invalid', this, msg);
9662     },
9663     // private
9664     SafariOnKeyDown : function(event)
9665     {
9666         // this is a workaround for a password hang bug on chrome/ webkit.
9667         if (this.inputEl().dom.type != 'password') {
9668             return;
9669         }
9670         
9671         var isSelectAll = false;
9672         
9673         if(this.inputEl().dom.selectionEnd > 0){
9674             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9675         }
9676         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9677             event.preventDefault();
9678             this.setValue('');
9679             return;
9680         }
9681         
9682         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9683             
9684             event.preventDefault();
9685             // this is very hacky as keydown always get's upper case.
9686             //
9687             var cc = String.fromCharCode(event.getCharCode());
9688             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9689             
9690         }
9691     },
9692     adjustWidth : function(tag, w){
9693         tag = tag.toLowerCase();
9694         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9695             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9696                 if(tag == 'input'){
9697                     return w + 2;
9698                 }
9699                 if(tag == 'textarea'){
9700                     return w-2;
9701                 }
9702             }else if(Roo.isOpera){
9703                 if(tag == 'input'){
9704                     return w + 2;
9705                 }
9706                 if(tag == 'textarea'){
9707                     return w-2;
9708                 }
9709             }
9710         }
9711         return w;
9712     },
9713     
9714     setFieldLabel : function(v)
9715     {
9716         if(!this.rendered){
9717             return;
9718         }
9719         
9720         if(this.indicator){
9721             var ar = this.el.select('label > span',true);
9722             
9723             if (ar.elements.length) {
9724                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9725                 this.fieldLabel = v;
9726                 return;
9727             }
9728             
9729             var br = this.el.select('label',true);
9730             
9731             if(br.elements.length) {
9732                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9733                 this.fieldLabel = v;
9734                 return;
9735             }
9736             
9737             Roo.log('Cannot Found any of label > span || label in input');
9738             return;
9739         }
9740         
9741         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9742         this.fieldLabel = v;
9743         
9744         
9745     }
9746 });
9747
9748  
9749 /*
9750  * - LGPL
9751  *
9752  * Input
9753  * 
9754  */
9755
9756 /**
9757  * @class Roo.bootstrap.TextArea
9758  * @extends Roo.bootstrap.Input
9759  * Bootstrap TextArea class
9760  * @cfg {Number} cols Specifies the visible width of a text area
9761  * @cfg {Number} rows Specifies the visible number of lines in a text area
9762  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9763  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9764  * @cfg {string} html text
9765  * 
9766  * @constructor
9767  * Create a new TextArea
9768  * @param {Object} config The config object
9769  */
9770
9771 Roo.bootstrap.TextArea = function(config){
9772     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9773    
9774 };
9775
9776 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9777      
9778     cols : false,
9779     rows : 5,
9780     readOnly : false,
9781     warp : 'soft',
9782     resize : false,
9783     value: false,
9784     html: false,
9785     
9786     getAutoCreate : function(){
9787         
9788         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9789         
9790         var id = Roo.id();
9791         
9792         var cfg = {};
9793         
9794         if(this.inputType != 'hidden'){
9795             cfg.cls = 'form-group' //input-group
9796         }
9797         
9798         var input =  {
9799             tag: 'textarea',
9800             id : id,
9801             warp : this.warp,
9802             rows : this.rows,
9803             value : this.value || '',
9804             html: this.html || '',
9805             cls : 'form-control',
9806             placeholder : this.placeholder || '' 
9807             
9808         };
9809         
9810         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9811             input.maxLength = this.maxLength;
9812         }
9813         
9814         if(this.resize){
9815             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9816         }
9817         
9818         if(this.cols){
9819             input.cols = this.cols;
9820         }
9821         
9822         if (this.readOnly) {
9823             input.readonly = true;
9824         }
9825         
9826         if (this.name) {
9827             input.name = this.name;
9828         }
9829         
9830         if (this.size) {
9831             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9832         }
9833         
9834         var settings=this;
9835         ['xs','sm','md','lg'].map(function(size){
9836             if (settings[size]) {
9837                 cfg.cls += ' col-' + size + '-' + settings[size];
9838             }
9839         });
9840         
9841         var inputblock = input;
9842         
9843         if(this.hasFeedback && !this.allowBlank){
9844             
9845             var feedback = {
9846                 tag: 'span',
9847                 cls: 'glyphicon form-control-feedback'
9848             };
9849
9850             inputblock = {
9851                 cls : 'has-feedback',
9852                 cn :  [
9853                     input,
9854                     feedback
9855                 ] 
9856             };  
9857         }
9858         
9859         
9860         if (this.before || this.after) {
9861             
9862             inputblock = {
9863                 cls : 'input-group',
9864                 cn :  [] 
9865             };
9866             if (this.before) {
9867                 inputblock.cn.push({
9868                     tag :'span',
9869                     cls : 'input-group-addon',
9870                     html : this.before
9871                 });
9872             }
9873             
9874             inputblock.cn.push(input);
9875             
9876             if(this.hasFeedback && !this.allowBlank){
9877                 inputblock.cls += ' has-feedback';
9878                 inputblock.cn.push(feedback);
9879             }
9880             
9881             if (this.after) {
9882                 inputblock.cn.push({
9883                     tag :'span',
9884                     cls : 'input-group-addon',
9885                     html : this.after
9886                 });
9887             }
9888             
9889         }
9890         
9891         if (align ==='left' && this.fieldLabel.length) {
9892             cfg.cn = [
9893                 {
9894                     tag: 'label',
9895                     'for' :  id,
9896                     cls : 'control-label',
9897                     html : this.fieldLabel
9898                 },
9899                 {
9900                     cls : "",
9901                     cn: [
9902                         inputblock
9903                     ]
9904                 }
9905
9906             ];
9907             
9908             if(this.labelWidth > 12){
9909                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9910             }
9911
9912             if(this.labelWidth < 13 && this.labelmd == 0){
9913                 this.labelmd = this.labelWidth;
9914             }
9915
9916             if(this.labellg > 0){
9917                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9918                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9919             }
9920
9921             if(this.labelmd > 0){
9922                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9923                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9924             }
9925
9926             if(this.labelsm > 0){
9927                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9928                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9929             }
9930
9931             if(this.labelxs > 0){
9932                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9933                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9934             }
9935             
9936         } else if ( this.fieldLabel.length) {
9937             cfg.cn = [
9938
9939                {
9940                    tag: 'label',
9941                    //cls : 'input-group-addon',
9942                    html : this.fieldLabel
9943
9944                },
9945
9946                inputblock
9947
9948            ];
9949
9950         } else {
9951
9952             cfg.cn = [
9953
9954                 inputblock
9955
9956             ];
9957                 
9958         }
9959         
9960         if (this.disabled) {
9961             input.disabled=true;
9962         }
9963         
9964         return cfg;
9965         
9966     },
9967     /**
9968      * return the real textarea element.
9969      */
9970     inputEl: function ()
9971     {
9972         return this.el.select('textarea.form-control',true).first();
9973     },
9974     
9975     /**
9976      * Clear any invalid styles/messages for this field
9977      */
9978     clearInvalid : function()
9979     {
9980         
9981         if(!this.el || this.preventMark){ // not rendered
9982             return;
9983         }
9984         
9985         var label = this.el.select('label', true).first();
9986         var icon = this.el.select('i.fa-star', true).first();
9987         
9988         if(label && icon){
9989             icon.remove();
9990         }
9991         
9992         this.el.removeClass(this.invalidClass);
9993         
9994         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9995             
9996             var feedback = this.el.select('.form-control-feedback', true).first();
9997             
9998             if(feedback){
9999                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10000             }
10001             
10002         }
10003         
10004         this.fireEvent('valid', this);
10005     },
10006     
10007      /**
10008      * Mark this field as valid
10009      */
10010     markValid : function()
10011     {
10012         if(!this.el  || this.preventMark){ // not rendered
10013             return;
10014         }
10015         
10016         this.el.removeClass([this.invalidClass, this.validClass]);
10017         
10018         var feedback = this.el.select('.form-control-feedback', true).first();
10019             
10020         if(feedback){
10021             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10022         }
10023
10024         if(this.disabled || this.allowBlank){
10025             return;
10026         }
10027         
10028         var label = this.el.select('label', true).first();
10029         var icon = this.el.select('i.fa-star', true).first();
10030         
10031         if(label && icon){
10032             icon.remove();
10033         }
10034         
10035         this.el.addClass(this.validClass);
10036         
10037         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10038             
10039             var feedback = this.el.select('.form-control-feedback', true).first();
10040             
10041             if(feedback){
10042                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10043                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10044             }
10045             
10046         }
10047         
10048         this.fireEvent('valid', this);
10049     },
10050     
10051      /**
10052      * Mark this field as invalid
10053      * @param {String} msg The validation message
10054      */
10055     markInvalid : function(msg)
10056     {
10057         if(!this.el  || this.preventMark){ // not rendered
10058             return;
10059         }
10060         
10061         this.el.removeClass([this.invalidClass, this.validClass]);
10062         
10063         var feedback = this.el.select('.form-control-feedback', true).first();
10064             
10065         if(feedback){
10066             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10067         }
10068
10069         if(this.disabled || this.allowBlank){
10070             return;
10071         }
10072         
10073         var label = this.el.select('label', true).first();
10074         var icon = this.el.select('i.fa-star', true).first();
10075         
10076         if(!this.getValue().length && label && !icon){
10077             this.el.createChild({
10078                 tag : 'i',
10079                 cls : 'text-danger fa fa-lg fa-star',
10080                 tooltip : 'This field is required',
10081                 style : 'margin-right:5px;'
10082             }, label, true);
10083         }
10084
10085         this.el.addClass(this.invalidClass);
10086         
10087         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10088             
10089             var feedback = this.el.select('.form-control-feedback', true).first();
10090             
10091             if(feedback){
10092                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10093                 
10094                 if(this.getValue().length || this.forceFeedback){
10095                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10096                 }
10097                 
10098             }
10099             
10100         }
10101         
10102         this.fireEvent('invalid', this, msg);
10103     }
10104 });
10105
10106  
10107 /*
10108  * - LGPL
10109  *
10110  * trigger field - base class for combo..
10111  * 
10112  */
10113  
10114 /**
10115  * @class Roo.bootstrap.TriggerField
10116  * @extends Roo.bootstrap.Input
10117  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10118  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10119  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10120  * for which you can provide a custom implementation.  For example:
10121  * <pre><code>
10122 var trigger = new Roo.bootstrap.TriggerField();
10123 trigger.onTriggerClick = myTriggerFn;
10124 trigger.applyTo('my-field');
10125 </code></pre>
10126  *
10127  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10128  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10129  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10130  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10131  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10132
10133  * @constructor
10134  * Create a new TriggerField.
10135  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10136  * to the base TextField)
10137  */
10138 Roo.bootstrap.TriggerField = function(config){
10139     this.mimicing = false;
10140     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10141 };
10142
10143 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10144     /**
10145      * @cfg {String} triggerClass A CSS class to apply to the trigger
10146      */
10147      /**
10148      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10149      */
10150     hideTrigger:false,
10151
10152     /**
10153      * @cfg {Boolean} removable (true|false) special filter default false
10154      */
10155     removable : false,
10156     
10157     /** @cfg {Boolean} grow @hide */
10158     /** @cfg {Number} growMin @hide */
10159     /** @cfg {Number} growMax @hide */
10160
10161     /**
10162      * @hide 
10163      * @method
10164      */
10165     autoSize: Roo.emptyFn,
10166     // private
10167     monitorTab : true,
10168     // private
10169     deferHeight : true,
10170
10171     
10172     actionMode : 'wrap',
10173     
10174     caret : false,
10175     
10176     
10177     getAutoCreate : function(){
10178        
10179         var align = this.labelAlign || this.parentLabelAlign();
10180         
10181         var id = Roo.id();
10182         
10183         var cfg = {
10184             cls: 'form-group' //input-group
10185         };
10186         
10187         
10188         var input =  {
10189             tag: 'input',
10190             id : id,
10191             type : this.inputType,
10192             cls : 'form-control',
10193             autocomplete: 'new-password',
10194             placeholder : this.placeholder || '' 
10195             
10196         };
10197         if (this.name) {
10198             input.name = this.name;
10199         }
10200         if (this.size) {
10201             input.cls += ' input-' + this.size;
10202         }
10203         
10204         if (this.disabled) {
10205             input.disabled=true;
10206         }
10207         
10208         var inputblock = input;
10209         
10210         if(this.hasFeedback && !this.allowBlank){
10211             
10212             var feedback = {
10213                 tag: 'span',
10214                 cls: 'glyphicon form-control-feedback'
10215             };
10216             
10217             if(this.removable && !this.editable && !this.tickable){
10218                 inputblock = {
10219                     cls : 'has-feedback',
10220                     cn :  [
10221                         inputblock,
10222                         {
10223                             tag: 'button',
10224                             html : 'x',
10225                             cls : 'roo-combo-removable-btn close'
10226                         },
10227                         feedback
10228                     ] 
10229                 };
10230             } else {
10231                 inputblock = {
10232                     cls : 'has-feedback',
10233                     cn :  [
10234                         inputblock,
10235                         feedback
10236                     ] 
10237                 };
10238             }
10239
10240         } else {
10241             if(this.removable && !this.editable && !this.tickable){
10242                 inputblock = {
10243                     cls : 'roo-removable',
10244                     cn :  [
10245                         inputblock,
10246                         {
10247                             tag: 'button',
10248                             html : 'x',
10249                             cls : 'roo-combo-removable-btn close'
10250                         }
10251                     ] 
10252                 };
10253             }
10254         }
10255         
10256         if (this.before || this.after) {
10257             
10258             inputblock = {
10259                 cls : 'input-group',
10260                 cn :  [] 
10261             };
10262             if (this.before) {
10263                 inputblock.cn.push({
10264                     tag :'span',
10265                     cls : 'input-group-addon',
10266                     html : this.before
10267                 });
10268             }
10269             
10270             inputblock.cn.push(input);
10271             
10272             if(this.hasFeedback && !this.allowBlank){
10273                 inputblock.cls += ' has-feedback';
10274                 inputblock.cn.push(feedback);
10275             }
10276             
10277             if (this.after) {
10278                 inputblock.cn.push({
10279                     tag :'span',
10280                     cls : 'input-group-addon',
10281                     html : this.after
10282                 });
10283             }
10284             
10285         };
10286         
10287         var box = {
10288             tag: 'div',
10289             cn: [
10290                 {
10291                     tag: 'input',
10292                     type : 'hidden',
10293                     cls: 'form-hidden-field'
10294                 },
10295                 inputblock
10296             ]
10297             
10298         };
10299         
10300         if(this.multiple){
10301             box = {
10302                 tag: 'div',
10303                 cn: [
10304                     {
10305                         tag: 'input',
10306                         type : 'hidden',
10307                         cls: 'form-hidden-field'
10308                     },
10309                     {
10310                         tag: 'ul',
10311                         cls: 'roo-select2-choices',
10312                         cn:[
10313                             {
10314                                 tag: 'li',
10315                                 cls: 'roo-select2-search-field',
10316                                 cn: [
10317
10318                                     inputblock
10319                                 ]
10320                             }
10321                         ]
10322                     }
10323                 ]
10324             }
10325         };
10326         
10327         var combobox = {
10328             cls: 'roo-select2-container input-group',
10329             cn: [
10330                 box
10331 //                {
10332 //                    tag: 'ul',
10333 //                    cls: 'typeahead typeahead-long dropdown-menu',
10334 //                    style: 'display:none'
10335 //                }
10336             ]
10337         };
10338         
10339         if(!this.multiple && this.showToggleBtn){
10340             
10341             var caret = {
10342                         tag: 'span',
10343                         cls: 'caret'
10344              };
10345             if (this.caret != false) {
10346                 caret = {
10347                      tag: 'i',
10348                      cls: 'fa fa-' + this.caret
10349                 };
10350                 
10351             }
10352             
10353             combobox.cn.push({
10354                 tag :'span',
10355                 cls : 'input-group-addon btn dropdown-toggle',
10356                 cn : [
10357                     caret,
10358                     {
10359                         tag: 'span',
10360                         cls: 'combobox-clear',
10361                         cn  : [
10362                             {
10363                                 tag : 'i',
10364                                 cls: 'icon-remove'
10365                             }
10366                         ]
10367                     }
10368                 ]
10369
10370             })
10371         }
10372         
10373         if(this.multiple){
10374             combobox.cls += ' roo-select2-container-multi';
10375         }
10376         
10377         if (align ==='left' && this.fieldLabel.length) {
10378             
10379             cfg.cls += ' roo-form-group-label-left';
10380
10381             cfg.cn = [
10382                 {
10383                     tag : 'i',
10384                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10385                     tooltip : 'This field is required'
10386                 },
10387                 {
10388                     tag: 'label',
10389                     'for' :  id,
10390                     cls : 'control-label',
10391                     html : this.fieldLabel
10392
10393                 },
10394                 {
10395                     cls : "", 
10396                     cn: [
10397                         combobox
10398                     ]
10399                 }
10400
10401             ];
10402             
10403             var labelCfg = cfg.cn[1];
10404             var contentCfg = cfg.cn[2];
10405             
10406             if(this.indicatorpos == 'right'){
10407                 cfg.cn = [
10408                     {
10409                         tag: 'label',
10410                         'for' :  id,
10411                         cls : 'control-label',
10412                         cn : [
10413                             {
10414                                 tag : 'span',
10415                                 html : this.fieldLabel
10416                             },
10417                             {
10418                                 tag : 'i',
10419                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10420                                 tooltip : 'This field is required'
10421                             }
10422                         ]
10423                     },
10424                     {
10425                         cls : "", 
10426                         cn: [
10427                             combobox
10428                         ]
10429                     }
10430
10431                 ];
10432                 
10433                 labelCfg = cfg.cn[0];
10434                 contentCfg = cfg.cn[1];
10435             }
10436             
10437             if(this.labelWidth > 12){
10438                 labelCfg.style = "width: " + this.labelWidth + 'px';
10439             }
10440             
10441             if(this.labelWidth < 13 && this.labelmd == 0){
10442                 this.labelmd = this.labelWidth;
10443             }
10444             
10445             if(this.labellg > 0){
10446                 labelCfg.cls += ' col-lg-' + this.labellg;
10447                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10448             }
10449             
10450             if(this.labelmd > 0){
10451                 labelCfg.cls += ' col-md-' + this.labelmd;
10452                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10453             }
10454             
10455             if(this.labelsm > 0){
10456                 labelCfg.cls += ' col-sm-' + this.labelsm;
10457                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10458             }
10459             
10460             if(this.labelxs > 0){
10461                 labelCfg.cls += ' col-xs-' + this.labelxs;
10462                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10463             }
10464             
10465         } else if ( this.fieldLabel.length) {
10466 //                Roo.log(" label");
10467             cfg.cn = [
10468                 {
10469                    tag : 'i',
10470                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10471                    tooltip : 'This field is required'
10472                },
10473                {
10474                    tag: 'label',
10475                    //cls : 'input-group-addon',
10476                    html : this.fieldLabel
10477
10478                },
10479
10480                combobox
10481
10482             ];
10483             
10484             if(this.indicatorpos == 'right'){
10485                 
10486                 cfg.cn = [
10487                     {
10488                        tag: 'label',
10489                        cn : [
10490                            {
10491                                tag : 'span',
10492                                html : this.fieldLabel
10493                            },
10494                            {
10495                               tag : 'i',
10496                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10497                               tooltip : 'This field is required'
10498                            }
10499                        ]
10500
10501                     },
10502                     combobox
10503
10504                 ];
10505
10506             }
10507
10508         } else {
10509             
10510 //                Roo.log(" no label && no align");
10511                 cfg = combobox
10512                      
10513                 
10514         }
10515         
10516         var settings=this;
10517         ['xs','sm','md','lg'].map(function(size){
10518             if (settings[size]) {
10519                 cfg.cls += ' col-' + size + '-' + settings[size];
10520             }
10521         });
10522         
10523         return cfg;
10524         
10525     },
10526     
10527     
10528     
10529     // private
10530     onResize : function(w, h){
10531 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10532 //        if(typeof w == 'number'){
10533 //            var x = w - this.trigger.getWidth();
10534 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10535 //            this.trigger.setStyle('left', x+'px');
10536 //        }
10537     },
10538
10539     // private
10540     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10541
10542     // private
10543     getResizeEl : function(){
10544         return this.inputEl();
10545     },
10546
10547     // private
10548     getPositionEl : function(){
10549         return this.inputEl();
10550     },
10551
10552     // private
10553     alignErrorIcon : function(){
10554         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10555     },
10556
10557     // private
10558     initEvents : function(){
10559         
10560         this.createList();
10561         
10562         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10563         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10564         if(!this.multiple && this.showToggleBtn){
10565             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10566             if(this.hideTrigger){
10567                 this.trigger.setDisplayed(false);
10568             }
10569             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10570         }
10571         
10572         if(this.multiple){
10573             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10574         }
10575         
10576         if(this.removable && !this.editable && !this.tickable){
10577             var close = this.closeTriggerEl();
10578             
10579             if(close){
10580                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10581                 close.on('click', this.removeBtnClick, this, close);
10582             }
10583         }
10584         
10585         //this.trigger.addClassOnOver('x-form-trigger-over');
10586         //this.trigger.addClassOnClick('x-form-trigger-click');
10587         
10588         //if(!this.width){
10589         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10590         //}
10591     },
10592     
10593     closeTriggerEl : function()
10594     {
10595         var close = this.el.select('.roo-combo-removable-btn', true).first();
10596         return close ? close : false;
10597     },
10598     
10599     removeBtnClick : function(e, h, el)
10600     {
10601         e.preventDefault();
10602         
10603         if(this.fireEvent("remove", this) !== false){
10604             this.reset();
10605             this.fireEvent("afterremove", this)
10606         }
10607     },
10608     
10609     createList : function()
10610     {
10611         this.list = Roo.get(document.body).createChild({
10612             tag: 'ul',
10613             cls: 'typeahead typeahead-long dropdown-menu',
10614             style: 'display:none'
10615         });
10616         
10617         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10618         
10619     },
10620
10621     // private
10622     initTrigger : function(){
10623        
10624     },
10625
10626     // private
10627     onDestroy : function(){
10628         if(this.trigger){
10629             this.trigger.removeAllListeners();
10630           //  this.trigger.remove();
10631         }
10632         //if(this.wrap){
10633         //    this.wrap.remove();
10634         //}
10635         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10636     },
10637
10638     // private
10639     onFocus : function(){
10640         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10641         /*
10642         if(!this.mimicing){
10643             this.wrap.addClass('x-trigger-wrap-focus');
10644             this.mimicing = true;
10645             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10646             if(this.monitorTab){
10647                 this.el.on("keydown", this.checkTab, this);
10648             }
10649         }
10650         */
10651     },
10652
10653     // private
10654     checkTab : function(e){
10655         if(e.getKey() == e.TAB){
10656             this.triggerBlur();
10657         }
10658     },
10659
10660     // private
10661     onBlur : function(){
10662         // do nothing
10663     },
10664
10665     // private
10666     mimicBlur : function(e, t){
10667         /*
10668         if(!this.wrap.contains(t) && this.validateBlur()){
10669             this.triggerBlur();
10670         }
10671         */
10672     },
10673
10674     // private
10675     triggerBlur : function(){
10676         this.mimicing = false;
10677         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10678         if(this.monitorTab){
10679             this.el.un("keydown", this.checkTab, this);
10680         }
10681         //this.wrap.removeClass('x-trigger-wrap-focus');
10682         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10683     },
10684
10685     // private
10686     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10687     validateBlur : function(e, t){
10688         return true;
10689     },
10690
10691     // private
10692     onDisable : function(){
10693         this.inputEl().dom.disabled = true;
10694         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10695         //if(this.wrap){
10696         //    this.wrap.addClass('x-item-disabled');
10697         //}
10698     },
10699
10700     // private
10701     onEnable : function(){
10702         this.inputEl().dom.disabled = false;
10703         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10704         //if(this.wrap){
10705         //    this.el.removeClass('x-item-disabled');
10706         //}
10707     },
10708
10709     // private
10710     onShow : function(){
10711         var ae = this.getActionEl();
10712         
10713         if(ae){
10714             ae.dom.style.display = '';
10715             ae.dom.style.visibility = 'visible';
10716         }
10717     },
10718
10719     // private
10720     
10721     onHide : function(){
10722         var ae = this.getActionEl();
10723         ae.dom.style.display = 'none';
10724     },
10725
10726     /**
10727      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10728      * by an implementing function.
10729      * @method
10730      * @param {EventObject} e
10731      */
10732     onTriggerClick : Roo.emptyFn
10733 });
10734  /*
10735  * Based on:
10736  * Ext JS Library 1.1.1
10737  * Copyright(c) 2006-2007, Ext JS, LLC.
10738  *
10739  * Originally Released Under LGPL - original licence link has changed is not relivant.
10740  *
10741  * Fork - LGPL
10742  * <script type="text/javascript">
10743  */
10744
10745
10746 /**
10747  * @class Roo.data.SortTypes
10748  * @singleton
10749  * Defines the default sorting (casting?) comparison functions used when sorting data.
10750  */
10751 Roo.data.SortTypes = {
10752     /**
10753      * Default sort that does nothing
10754      * @param {Mixed} s The value being converted
10755      * @return {Mixed} The comparison value
10756      */
10757     none : function(s){
10758         return s;
10759     },
10760     
10761     /**
10762      * The regular expression used to strip tags
10763      * @type {RegExp}
10764      * @property
10765      */
10766     stripTagsRE : /<\/?[^>]+>/gi,
10767     
10768     /**
10769      * Strips all HTML tags to sort on text only
10770      * @param {Mixed} s The value being converted
10771      * @return {String} The comparison value
10772      */
10773     asText : function(s){
10774         return String(s).replace(this.stripTagsRE, "");
10775     },
10776     
10777     /**
10778      * Strips all HTML tags to sort on text only - Case insensitive
10779      * @param {Mixed} s The value being converted
10780      * @return {String} The comparison value
10781      */
10782     asUCText : function(s){
10783         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10784     },
10785     
10786     /**
10787      * Case insensitive string
10788      * @param {Mixed} s The value being converted
10789      * @return {String} The comparison value
10790      */
10791     asUCString : function(s) {
10792         return String(s).toUpperCase();
10793     },
10794     
10795     /**
10796      * Date sorting
10797      * @param {Mixed} s The value being converted
10798      * @return {Number} The comparison value
10799      */
10800     asDate : function(s) {
10801         if(!s){
10802             return 0;
10803         }
10804         if(s instanceof Date){
10805             return s.getTime();
10806         }
10807         return Date.parse(String(s));
10808     },
10809     
10810     /**
10811      * Float sorting
10812      * @param {Mixed} s The value being converted
10813      * @return {Float} The comparison value
10814      */
10815     asFloat : function(s) {
10816         var val = parseFloat(String(s).replace(/,/g, ""));
10817         if(isNaN(val)) {
10818             val = 0;
10819         }
10820         return val;
10821     },
10822     
10823     /**
10824      * Integer sorting
10825      * @param {Mixed} s The value being converted
10826      * @return {Number} The comparison value
10827      */
10828     asInt : function(s) {
10829         var val = parseInt(String(s).replace(/,/g, ""));
10830         if(isNaN(val)) {
10831             val = 0;
10832         }
10833         return val;
10834     }
10835 };/*
10836  * Based on:
10837  * Ext JS Library 1.1.1
10838  * Copyright(c) 2006-2007, Ext JS, LLC.
10839  *
10840  * Originally Released Under LGPL - original licence link has changed is not relivant.
10841  *
10842  * Fork - LGPL
10843  * <script type="text/javascript">
10844  */
10845
10846 /**
10847 * @class Roo.data.Record
10848  * Instances of this class encapsulate both record <em>definition</em> information, and record
10849  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10850  * to access Records cached in an {@link Roo.data.Store} object.<br>
10851  * <p>
10852  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10853  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10854  * objects.<br>
10855  * <p>
10856  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10857  * @constructor
10858  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10859  * {@link #create}. The parameters are the same.
10860  * @param {Array} data An associative Array of data values keyed by the field name.
10861  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10862  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10863  * not specified an integer id is generated.
10864  */
10865 Roo.data.Record = function(data, id){
10866     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10867     this.data = data;
10868 };
10869
10870 /**
10871  * Generate a constructor for a specific record layout.
10872  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10873  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10874  * Each field definition object may contain the following properties: <ul>
10875  * <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,
10876  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10877  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10878  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10879  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10880  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10881  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10882  * this may be omitted.</p></li>
10883  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10884  * <ul><li>auto (Default, implies no conversion)</li>
10885  * <li>string</li>
10886  * <li>int</li>
10887  * <li>float</li>
10888  * <li>boolean</li>
10889  * <li>date</li></ul></p></li>
10890  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10891  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10892  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10893  * by the Reader into an object that will be stored in the Record. It is passed the
10894  * following parameters:<ul>
10895  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10896  * </ul></p></li>
10897  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10898  * </ul>
10899  * <br>usage:<br><pre><code>
10900 var TopicRecord = Roo.data.Record.create(
10901     {name: 'title', mapping: 'topic_title'},
10902     {name: 'author', mapping: 'username'},
10903     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10904     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10905     {name: 'lastPoster', mapping: 'user2'},
10906     {name: 'excerpt', mapping: 'post_text'}
10907 );
10908
10909 var myNewRecord = new TopicRecord({
10910     title: 'Do my job please',
10911     author: 'noobie',
10912     totalPosts: 1,
10913     lastPost: new Date(),
10914     lastPoster: 'Animal',
10915     excerpt: 'No way dude!'
10916 });
10917 myStore.add(myNewRecord);
10918 </code></pre>
10919  * @method create
10920  * @static
10921  */
10922 Roo.data.Record.create = function(o){
10923     var f = function(){
10924         f.superclass.constructor.apply(this, arguments);
10925     };
10926     Roo.extend(f, Roo.data.Record);
10927     var p = f.prototype;
10928     p.fields = new Roo.util.MixedCollection(false, function(field){
10929         return field.name;
10930     });
10931     for(var i = 0, len = o.length; i < len; i++){
10932         p.fields.add(new Roo.data.Field(o[i]));
10933     }
10934     f.getField = function(name){
10935         return p.fields.get(name);  
10936     };
10937     return f;
10938 };
10939
10940 Roo.data.Record.AUTO_ID = 1000;
10941 Roo.data.Record.EDIT = 'edit';
10942 Roo.data.Record.REJECT = 'reject';
10943 Roo.data.Record.COMMIT = 'commit';
10944
10945 Roo.data.Record.prototype = {
10946     /**
10947      * Readonly flag - true if this record has been modified.
10948      * @type Boolean
10949      */
10950     dirty : false,
10951     editing : false,
10952     error: null,
10953     modified: null,
10954
10955     // private
10956     join : function(store){
10957         this.store = store;
10958     },
10959
10960     /**
10961      * Set the named field to the specified value.
10962      * @param {String} name The name of the field to set.
10963      * @param {Object} value The value to set the field to.
10964      */
10965     set : function(name, value){
10966         if(this.data[name] == value){
10967             return;
10968         }
10969         this.dirty = true;
10970         if(!this.modified){
10971             this.modified = {};
10972         }
10973         if(typeof this.modified[name] == 'undefined'){
10974             this.modified[name] = this.data[name];
10975         }
10976         this.data[name] = value;
10977         if(!this.editing && this.store){
10978             this.store.afterEdit(this);
10979         }       
10980     },
10981
10982     /**
10983      * Get the value of the named field.
10984      * @param {String} name The name of the field to get the value of.
10985      * @return {Object} The value of the field.
10986      */
10987     get : function(name){
10988         return this.data[name]; 
10989     },
10990
10991     // private
10992     beginEdit : function(){
10993         this.editing = true;
10994         this.modified = {}; 
10995     },
10996
10997     // private
10998     cancelEdit : function(){
10999         this.editing = false;
11000         delete this.modified;
11001     },
11002
11003     // private
11004     endEdit : function(){
11005         this.editing = false;
11006         if(this.dirty && this.store){
11007             this.store.afterEdit(this);
11008         }
11009     },
11010
11011     /**
11012      * Usually called by the {@link Roo.data.Store} which owns the Record.
11013      * Rejects all changes made to the Record since either creation, or the last commit operation.
11014      * Modified fields are reverted to their original values.
11015      * <p>
11016      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11017      * of reject operations.
11018      */
11019     reject : function(){
11020         var m = this.modified;
11021         for(var n in m){
11022             if(typeof m[n] != "function"){
11023                 this.data[n] = m[n];
11024             }
11025         }
11026         this.dirty = false;
11027         delete this.modified;
11028         this.editing = false;
11029         if(this.store){
11030             this.store.afterReject(this);
11031         }
11032     },
11033
11034     /**
11035      * Usually called by the {@link Roo.data.Store} which owns the Record.
11036      * Commits all changes made to the Record since either creation, or the last commit operation.
11037      * <p>
11038      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11039      * of commit operations.
11040      */
11041     commit : function(){
11042         this.dirty = false;
11043         delete this.modified;
11044         this.editing = false;
11045         if(this.store){
11046             this.store.afterCommit(this);
11047         }
11048     },
11049
11050     // private
11051     hasError : function(){
11052         return this.error != null;
11053     },
11054
11055     // private
11056     clearError : function(){
11057         this.error = null;
11058     },
11059
11060     /**
11061      * Creates a copy of this record.
11062      * @param {String} id (optional) A new record id if you don't want to use this record's id
11063      * @return {Record}
11064      */
11065     copy : function(newId) {
11066         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11067     }
11068 };/*
11069  * Based on:
11070  * Ext JS Library 1.1.1
11071  * Copyright(c) 2006-2007, Ext JS, LLC.
11072  *
11073  * Originally Released Under LGPL - original licence link has changed is not relivant.
11074  *
11075  * Fork - LGPL
11076  * <script type="text/javascript">
11077  */
11078
11079
11080
11081 /**
11082  * @class Roo.data.Store
11083  * @extends Roo.util.Observable
11084  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11085  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11086  * <p>
11087  * 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
11088  * has no knowledge of the format of the data returned by the Proxy.<br>
11089  * <p>
11090  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11091  * instances from the data object. These records are cached and made available through accessor functions.
11092  * @constructor
11093  * Creates a new Store.
11094  * @param {Object} config A config object containing the objects needed for the Store to access data,
11095  * and read the data into Records.
11096  */
11097 Roo.data.Store = function(config){
11098     this.data = new Roo.util.MixedCollection(false);
11099     this.data.getKey = function(o){
11100         return o.id;
11101     };
11102     this.baseParams = {};
11103     // private
11104     this.paramNames = {
11105         "start" : "start",
11106         "limit" : "limit",
11107         "sort" : "sort",
11108         "dir" : "dir",
11109         "multisort" : "_multisort"
11110     };
11111
11112     if(config && config.data){
11113         this.inlineData = config.data;
11114         delete config.data;
11115     }
11116
11117     Roo.apply(this, config);
11118     
11119     if(this.reader){ // reader passed
11120         this.reader = Roo.factory(this.reader, Roo.data);
11121         this.reader.xmodule = this.xmodule || false;
11122         if(!this.recordType){
11123             this.recordType = this.reader.recordType;
11124         }
11125         if(this.reader.onMetaChange){
11126             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11127         }
11128     }
11129
11130     if(this.recordType){
11131         this.fields = this.recordType.prototype.fields;
11132     }
11133     this.modified = [];
11134
11135     this.addEvents({
11136         /**
11137          * @event datachanged
11138          * Fires when the data cache has changed, and a widget which is using this Store
11139          * as a Record cache should refresh its view.
11140          * @param {Store} this
11141          */
11142         datachanged : true,
11143         /**
11144          * @event metachange
11145          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11146          * @param {Store} this
11147          * @param {Object} meta The JSON metadata
11148          */
11149         metachange : true,
11150         /**
11151          * @event add
11152          * Fires when Records have been added to the Store
11153          * @param {Store} this
11154          * @param {Roo.data.Record[]} records The array of Records added
11155          * @param {Number} index The index at which the record(s) were added
11156          */
11157         add : true,
11158         /**
11159          * @event remove
11160          * Fires when a Record has been removed from the Store
11161          * @param {Store} this
11162          * @param {Roo.data.Record} record The Record that was removed
11163          * @param {Number} index The index at which the record was removed
11164          */
11165         remove : true,
11166         /**
11167          * @event update
11168          * Fires when a Record has been updated
11169          * @param {Store} this
11170          * @param {Roo.data.Record} record The Record that was updated
11171          * @param {String} operation The update operation being performed.  Value may be one of:
11172          * <pre><code>
11173  Roo.data.Record.EDIT
11174  Roo.data.Record.REJECT
11175  Roo.data.Record.COMMIT
11176          * </code></pre>
11177          */
11178         update : true,
11179         /**
11180          * @event clear
11181          * Fires when the data cache has been cleared.
11182          * @param {Store} this
11183          */
11184         clear : true,
11185         /**
11186          * @event beforeload
11187          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11188          * the load action will be canceled.
11189          * @param {Store} this
11190          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11191          */
11192         beforeload : true,
11193         /**
11194          * @event beforeloadadd
11195          * Fires after a new set of Records has been loaded.
11196          * @param {Store} this
11197          * @param {Roo.data.Record[]} records The Records that were loaded
11198          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11199          */
11200         beforeloadadd : true,
11201         /**
11202          * @event load
11203          * Fires after a new set of Records has been loaded, before they are added to the store.
11204          * @param {Store} this
11205          * @param {Roo.data.Record[]} records The Records that were loaded
11206          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11207          * @params {Object} return from reader
11208          */
11209         load : true,
11210         /**
11211          * @event loadexception
11212          * Fires if an exception occurs in the Proxy during loading.
11213          * Called with the signature of the Proxy's "loadexception" event.
11214          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11215          * 
11216          * @param {Proxy} 
11217          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11218          * @param {Object} load options 
11219          * @param {Object} jsonData from your request (normally this contains the Exception)
11220          */
11221         loadexception : true
11222     });
11223     
11224     if(this.proxy){
11225         this.proxy = Roo.factory(this.proxy, Roo.data);
11226         this.proxy.xmodule = this.xmodule || false;
11227         this.relayEvents(this.proxy,  ["loadexception"]);
11228     }
11229     this.sortToggle = {};
11230     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11231
11232     Roo.data.Store.superclass.constructor.call(this);
11233
11234     if(this.inlineData){
11235         this.loadData(this.inlineData);
11236         delete this.inlineData;
11237     }
11238 };
11239
11240 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11241      /**
11242     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11243     * without a remote query - used by combo/forms at present.
11244     */
11245     
11246     /**
11247     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11248     */
11249     /**
11250     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11251     */
11252     /**
11253     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11254     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11255     */
11256     /**
11257     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11258     * on any HTTP request
11259     */
11260     /**
11261     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11262     */
11263     /**
11264     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11265     */
11266     multiSort: false,
11267     /**
11268     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11269     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11270     */
11271     remoteSort : false,
11272
11273     /**
11274     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11275      * loaded or when a record is removed. (defaults to false).
11276     */
11277     pruneModifiedRecords : false,
11278
11279     // private
11280     lastOptions : null,
11281
11282     /**
11283      * Add Records to the Store and fires the add event.
11284      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11285      */
11286     add : function(records){
11287         records = [].concat(records);
11288         for(var i = 0, len = records.length; i < len; i++){
11289             records[i].join(this);
11290         }
11291         var index = this.data.length;
11292         this.data.addAll(records);
11293         this.fireEvent("add", this, records, index);
11294     },
11295
11296     /**
11297      * Remove a Record from the Store and fires the remove event.
11298      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11299      */
11300     remove : function(record){
11301         var index = this.data.indexOf(record);
11302         this.data.removeAt(index);
11303  
11304         if(this.pruneModifiedRecords){
11305             this.modified.remove(record);
11306         }
11307         this.fireEvent("remove", this, record, index);
11308     },
11309
11310     /**
11311      * Remove all Records from the Store and fires the clear event.
11312      */
11313     removeAll : function(){
11314         this.data.clear();
11315         if(this.pruneModifiedRecords){
11316             this.modified = [];
11317         }
11318         this.fireEvent("clear", this);
11319     },
11320
11321     /**
11322      * Inserts Records to the Store at the given index and fires the add event.
11323      * @param {Number} index The start index at which to insert the passed Records.
11324      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11325      */
11326     insert : function(index, records){
11327         records = [].concat(records);
11328         for(var i = 0, len = records.length; i < len; i++){
11329             this.data.insert(index, records[i]);
11330             records[i].join(this);
11331         }
11332         this.fireEvent("add", this, records, index);
11333     },
11334
11335     /**
11336      * Get the index within the cache of the passed Record.
11337      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11338      * @return {Number} The index of the passed Record. Returns -1 if not found.
11339      */
11340     indexOf : function(record){
11341         return this.data.indexOf(record);
11342     },
11343
11344     /**
11345      * Get the index within the cache of the Record with the passed id.
11346      * @param {String} id The id of the Record to find.
11347      * @return {Number} The index of the Record. Returns -1 if not found.
11348      */
11349     indexOfId : function(id){
11350         return this.data.indexOfKey(id);
11351     },
11352
11353     /**
11354      * Get the Record with the specified id.
11355      * @param {String} id The id of the Record to find.
11356      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11357      */
11358     getById : function(id){
11359         return this.data.key(id);
11360     },
11361
11362     /**
11363      * Get the Record at the specified index.
11364      * @param {Number} index The index of the Record to find.
11365      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11366      */
11367     getAt : function(index){
11368         return this.data.itemAt(index);
11369     },
11370
11371     /**
11372      * Returns a range of Records between specified indices.
11373      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11374      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11375      * @return {Roo.data.Record[]} An array of Records
11376      */
11377     getRange : function(start, end){
11378         return this.data.getRange(start, end);
11379     },
11380
11381     // private
11382     storeOptions : function(o){
11383         o = Roo.apply({}, o);
11384         delete o.callback;
11385         delete o.scope;
11386         this.lastOptions = o;
11387     },
11388
11389     /**
11390      * Loads the Record cache from the configured Proxy using the configured Reader.
11391      * <p>
11392      * If using remote paging, then the first load call must specify the <em>start</em>
11393      * and <em>limit</em> properties in the options.params property to establish the initial
11394      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11395      * <p>
11396      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11397      * and this call will return before the new data has been loaded. Perform any post-processing
11398      * in a callback function, or in a "load" event handler.</strong>
11399      * <p>
11400      * @param {Object} options An object containing properties which control loading options:<ul>
11401      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11402      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11403      * passed the following arguments:<ul>
11404      * <li>r : Roo.data.Record[]</li>
11405      * <li>options: Options object from the load call</li>
11406      * <li>success: Boolean success indicator</li></ul></li>
11407      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11408      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11409      * </ul>
11410      */
11411     load : function(options){
11412         options = options || {};
11413         if(this.fireEvent("beforeload", this, options) !== false){
11414             this.storeOptions(options);
11415             var p = Roo.apply(options.params || {}, this.baseParams);
11416             // if meta was not loaded from remote source.. try requesting it.
11417             if (!this.reader.metaFromRemote) {
11418                 p._requestMeta = 1;
11419             }
11420             if(this.sortInfo && this.remoteSort){
11421                 var pn = this.paramNames;
11422                 p[pn["sort"]] = this.sortInfo.field;
11423                 p[pn["dir"]] = this.sortInfo.direction;
11424             }
11425             if (this.multiSort) {
11426                 var pn = this.paramNames;
11427                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11428             }
11429             
11430             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11431         }
11432     },
11433
11434     /**
11435      * Reloads the Record cache from the configured Proxy using the configured Reader and
11436      * the options from the last load operation performed.
11437      * @param {Object} options (optional) An object containing properties which may override the options
11438      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11439      * the most recently used options are reused).
11440      */
11441     reload : function(options){
11442         this.load(Roo.applyIf(options||{}, this.lastOptions));
11443     },
11444
11445     // private
11446     // Called as a callback by the Reader during a load operation.
11447     loadRecords : function(o, options, success){
11448         if(!o || success === false){
11449             if(success !== false){
11450                 this.fireEvent("load", this, [], options, o);
11451             }
11452             if(options.callback){
11453                 options.callback.call(options.scope || this, [], options, false);
11454             }
11455             return;
11456         }
11457         // if data returned failure - throw an exception.
11458         if (o.success === false) {
11459             // show a message if no listener is registered.
11460             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11461                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11462             }
11463             // loadmask wil be hooked into this..
11464             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11465             return;
11466         }
11467         var r = o.records, t = o.totalRecords || r.length;
11468         
11469         this.fireEvent("beforeloadadd", this, r, options, o);
11470         
11471         if(!options || options.add !== true){
11472             if(this.pruneModifiedRecords){
11473                 this.modified = [];
11474             }
11475             for(var i = 0, len = r.length; i < len; i++){
11476                 r[i].join(this);
11477             }
11478             if(this.snapshot){
11479                 this.data = this.snapshot;
11480                 delete this.snapshot;
11481             }
11482             this.data.clear();
11483             this.data.addAll(r);
11484             this.totalLength = t;
11485             this.applySort();
11486             this.fireEvent("datachanged", this);
11487         }else{
11488             this.totalLength = Math.max(t, this.data.length+r.length);
11489             this.add(r);
11490         }
11491         
11492         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11493                 
11494             var e = new Roo.data.Record({});
11495
11496             e.set(this.parent.displayField, this.parent.emptyTitle);
11497             e.set(this.parent.valueField, '');
11498
11499             this.insert(0, e);
11500         }
11501             
11502         this.fireEvent("load", this, r, options, o);
11503         if(options.callback){
11504             options.callback.call(options.scope || this, r, options, true);
11505         }
11506     },
11507
11508
11509     /**
11510      * Loads data from a passed data block. A Reader which understands the format of the data
11511      * must have been configured in the constructor.
11512      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11513      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11514      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11515      */
11516     loadData : function(o, append){
11517         var r = this.reader.readRecords(o);
11518         this.loadRecords(r, {add: append}, true);
11519     },
11520
11521     /**
11522      * Gets the number of cached records.
11523      * <p>
11524      * <em>If using paging, this may not be the total size of the dataset. If the data object
11525      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11526      * the data set size</em>
11527      */
11528     getCount : function(){
11529         return this.data.length || 0;
11530     },
11531
11532     /**
11533      * Gets the total number of records in the dataset as returned by the server.
11534      * <p>
11535      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11536      * the dataset size</em>
11537      */
11538     getTotalCount : function(){
11539         return this.totalLength || 0;
11540     },
11541
11542     /**
11543      * Returns the sort state of the Store as an object with two properties:
11544      * <pre><code>
11545  field {String} The name of the field by which the Records are sorted
11546  direction {String} The sort order, "ASC" or "DESC"
11547      * </code></pre>
11548      */
11549     getSortState : function(){
11550         return this.sortInfo;
11551     },
11552
11553     // private
11554     applySort : function(){
11555         if(this.sortInfo && !this.remoteSort){
11556             var s = this.sortInfo, f = s.field;
11557             var st = this.fields.get(f).sortType;
11558             var fn = function(r1, r2){
11559                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11560                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11561             };
11562             this.data.sort(s.direction, fn);
11563             if(this.snapshot && this.snapshot != this.data){
11564                 this.snapshot.sort(s.direction, fn);
11565             }
11566         }
11567     },
11568
11569     /**
11570      * Sets the default sort column and order to be used by the next load operation.
11571      * @param {String} fieldName The name of the field to sort by.
11572      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11573      */
11574     setDefaultSort : function(field, dir){
11575         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11576     },
11577
11578     /**
11579      * Sort the Records.
11580      * If remote sorting is used, the sort is performed on the server, and the cache is
11581      * reloaded. If local sorting is used, the cache is sorted internally.
11582      * @param {String} fieldName The name of the field to sort by.
11583      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11584      */
11585     sort : function(fieldName, dir){
11586         var f = this.fields.get(fieldName);
11587         if(!dir){
11588             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11589             
11590             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11591                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11592             }else{
11593                 dir = f.sortDir;
11594             }
11595         }
11596         this.sortToggle[f.name] = dir;
11597         this.sortInfo = {field: f.name, direction: dir};
11598         if(!this.remoteSort){
11599             this.applySort();
11600             this.fireEvent("datachanged", this);
11601         }else{
11602             this.load(this.lastOptions);
11603         }
11604     },
11605
11606     /**
11607      * Calls the specified function for each of the Records in the cache.
11608      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11609      * Returning <em>false</em> aborts and exits the iteration.
11610      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11611      */
11612     each : function(fn, scope){
11613         this.data.each(fn, scope);
11614     },
11615
11616     /**
11617      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11618      * (e.g., during paging).
11619      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11620      */
11621     getModifiedRecords : function(){
11622         return this.modified;
11623     },
11624
11625     // private
11626     createFilterFn : function(property, value, anyMatch){
11627         if(!value.exec){ // not a regex
11628             value = String(value);
11629             if(value.length == 0){
11630                 return false;
11631             }
11632             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11633         }
11634         return function(r){
11635             return value.test(r.data[property]);
11636         };
11637     },
11638
11639     /**
11640      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11641      * @param {String} property A field on your records
11642      * @param {Number} start The record index to start at (defaults to 0)
11643      * @param {Number} end The last record index to include (defaults to length - 1)
11644      * @return {Number} The sum
11645      */
11646     sum : function(property, start, end){
11647         var rs = this.data.items, v = 0;
11648         start = start || 0;
11649         end = (end || end === 0) ? end : rs.length-1;
11650
11651         for(var i = start; i <= end; i++){
11652             v += (rs[i].data[property] || 0);
11653         }
11654         return v;
11655     },
11656
11657     /**
11658      * Filter the records by a specified property.
11659      * @param {String} field A field on your records
11660      * @param {String/RegExp} value Either a string that the field
11661      * should start with or a RegExp to test against the field
11662      * @param {Boolean} anyMatch True to match any part not just the beginning
11663      */
11664     filter : function(property, value, anyMatch){
11665         var fn = this.createFilterFn(property, value, anyMatch);
11666         return fn ? this.filterBy(fn) : this.clearFilter();
11667     },
11668
11669     /**
11670      * Filter by a function. The specified function will be called with each
11671      * record in this data source. If the function returns true the record is included,
11672      * otherwise it is filtered.
11673      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11674      * @param {Object} scope (optional) The scope of the function (defaults to this)
11675      */
11676     filterBy : function(fn, scope){
11677         this.snapshot = this.snapshot || this.data;
11678         this.data = this.queryBy(fn, scope||this);
11679         this.fireEvent("datachanged", this);
11680     },
11681
11682     /**
11683      * Query the records by a specified property.
11684      * @param {String} field A field on your records
11685      * @param {String/RegExp} value Either a string that the field
11686      * should start with or a RegExp to test against the field
11687      * @param {Boolean} anyMatch True to match any part not just the beginning
11688      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11689      */
11690     query : function(property, value, anyMatch){
11691         var fn = this.createFilterFn(property, value, anyMatch);
11692         return fn ? this.queryBy(fn) : this.data.clone();
11693     },
11694
11695     /**
11696      * Query by a function. The specified function will be called with each
11697      * record in this data source. If the function returns true the record is included
11698      * in the results.
11699      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11700      * @param {Object} scope (optional) The scope of the function (defaults to this)
11701       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11702      **/
11703     queryBy : function(fn, scope){
11704         var data = this.snapshot || this.data;
11705         return data.filterBy(fn, scope||this);
11706     },
11707
11708     /**
11709      * Collects unique values for a particular dataIndex from this store.
11710      * @param {String} dataIndex The property to collect
11711      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11712      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11713      * @return {Array} An array of the unique values
11714      **/
11715     collect : function(dataIndex, allowNull, bypassFilter){
11716         var d = (bypassFilter === true && this.snapshot) ?
11717                 this.snapshot.items : this.data.items;
11718         var v, sv, r = [], l = {};
11719         for(var i = 0, len = d.length; i < len; i++){
11720             v = d[i].data[dataIndex];
11721             sv = String(v);
11722             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11723                 l[sv] = true;
11724                 r[r.length] = v;
11725             }
11726         }
11727         return r;
11728     },
11729
11730     /**
11731      * Revert to a view of the Record cache with no filtering applied.
11732      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11733      */
11734     clearFilter : function(suppressEvent){
11735         if(this.snapshot && this.snapshot != this.data){
11736             this.data = this.snapshot;
11737             delete this.snapshot;
11738             if(suppressEvent !== true){
11739                 this.fireEvent("datachanged", this);
11740             }
11741         }
11742     },
11743
11744     // private
11745     afterEdit : function(record){
11746         if(this.modified.indexOf(record) == -1){
11747             this.modified.push(record);
11748         }
11749         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11750     },
11751     
11752     // private
11753     afterReject : function(record){
11754         this.modified.remove(record);
11755         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11756     },
11757
11758     // private
11759     afterCommit : function(record){
11760         this.modified.remove(record);
11761         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11762     },
11763
11764     /**
11765      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11766      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11767      */
11768     commitChanges : function(){
11769         var m = this.modified.slice(0);
11770         this.modified = [];
11771         for(var i = 0, len = m.length; i < len; i++){
11772             m[i].commit();
11773         }
11774     },
11775
11776     /**
11777      * Cancel outstanding changes on all changed records.
11778      */
11779     rejectChanges : function(){
11780         var m = this.modified.slice(0);
11781         this.modified = [];
11782         for(var i = 0, len = m.length; i < len; i++){
11783             m[i].reject();
11784         }
11785     },
11786
11787     onMetaChange : function(meta, rtype, o){
11788         this.recordType = rtype;
11789         this.fields = rtype.prototype.fields;
11790         delete this.snapshot;
11791         this.sortInfo = meta.sortInfo || this.sortInfo;
11792         this.modified = [];
11793         this.fireEvent('metachange', this, this.reader.meta);
11794     },
11795     
11796     moveIndex : function(data, type)
11797     {
11798         var index = this.indexOf(data);
11799         
11800         var newIndex = index + type;
11801         
11802         this.remove(data);
11803         
11804         this.insert(newIndex, data);
11805         
11806     }
11807 });/*
11808  * Based on:
11809  * Ext JS Library 1.1.1
11810  * Copyright(c) 2006-2007, Ext JS, LLC.
11811  *
11812  * Originally Released Under LGPL - original licence link has changed is not relivant.
11813  *
11814  * Fork - LGPL
11815  * <script type="text/javascript">
11816  */
11817
11818 /**
11819  * @class Roo.data.SimpleStore
11820  * @extends Roo.data.Store
11821  * Small helper class to make creating Stores from Array data easier.
11822  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11823  * @cfg {Array} fields An array of field definition objects, or field name strings.
11824  * @cfg {Array} data The multi-dimensional array of data
11825  * @constructor
11826  * @param {Object} config
11827  */
11828 Roo.data.SimpleStore = function(config){
11829     Roo.data.SimpleStore.superclass.constructor.call(this, {
11830         isLocal : true,
11831         reader: new Roo.data.ArrayReader({
11832                 id: config.id
11833             },
11834             Roo.data.Record.create(config.fields)
11835         ),
11836         proxy : new Roo.data.MemoryProxy(config.data)
11837     });
11838     this.load();
11839 };
11840 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11841  * Based on:
11842  * Ext JS Library 1.1.1
11843  * Copyright(c) 2006-2007, Ext JS, LLC.
11844  *
11845  * Originally Released Under LGPL - original licence link has changed is not relivant.
11846  *
11847  * Fork - LGPL
11848  * <script type="text/javascript">
11849  */
11850
11851 /**
11852 /**
11853  * @extends Roo.data.Store
11854  * @class Roo.data.JsonStore
11855  * Small helper class to make creating Stores for JSON data easier. <br/>
11856 <pre><code>
11857 var store = new Roo.data.JsonStore({
11858     url: 'get-images.php',
11859     root: 'images',
11860     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11861 });
11862 </code></pre>
11863  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11864  * JsonReader and HttpProxy (unless inline data is provided).</b>
11865  * @cfg {Array} fields An array of field definition objects, or field name strings.
11866  * @constructor
11867  * @param {Object} config
11868  */
11869 Roo.data.JsonStore = function(c){
11870     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11871         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11872         reader: new Roo.data.JsonReader(c, c.fields)
11873     }));
11874 };
11875 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11876  * Based on:
11877  * Ext JS Library 1.1.1
11878  * Copyright(c) 2006-2007, Ext JS, LLC.
11879  *
11880  * Originally Released Under LGPL - original licence link has changed is not relivant.
11881  *
11882  * Fork - LGPL
11883  * <script type="text/javascript">
11884  */
11885
11886  
11887 Roo.data.Field = function(config){
11888     if(typeof config == "string"){
11889         config = {name: config};
11890     }
11891     Roo.apply(this, config);
11892     
11893     if(!this.type){
11894         this.type = "auto";
11895     }
11896     
11897     var st = Roo.data.SortTypes;
11898     // named sortTypes are supported, here we look them up
11899     if(typeof this.sortType == "string"){
11900         this.sortType = st[this.sortType];
11901     }
11902     
11903     // set default sortType for strings and dates
11904     if(!this.sortType){
11905         switch(this.type){
11906             case "string":
11907                 this.sortType = st.asUCString;
11908                 break;
11909             case "date":
11910                 this.sortType = st.asDate;
11911                 break;
11912             default:
11913                 this.sortType = st.none;
11914         }
11915     }
11916
11917     // define once
11918     var stripRe = /[\$,%]/g;
11919
11920     // prebuilt conversion function for this field, instead of
11921     // switching every time we're reading a value
11922     if(!this.convert){
11923         var cv, dateFormat = this.dateFormat;
11924         switch(this.type){
11925             case "":
11926             case "auto":
11927             case undefined:
11928                 cv = function(v){ return v; };
11929                 break;
11930             case "string":
11931                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11932                 break;
11933             case "int":
11934                 cv = function(v){
11935                     return v !== undefined && v !== null && v !== '' ?
11936                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11937                     };
11938                 break;
11939             case "float":
11940                 cv = function(v){
11941                     return v !== undefined && v !== null && v !== '' ?
11942                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11943                     };
11944                 break;
11945             case "bool":
11946             case "boolean":
11947                 cv = function(v){ return v === true || v === "true" || v == 1; };
11948                 break;
11949             case "date":
11950                 cv = function(v){
11951                     if(!v){
11952                         return '';
11953                     }
11954                     if(v instanceof Date){
11955                         return v;
11956                     }
11957                     if(dateFormat){
11958                         if(dateFormat == "timestamp"){
11959                             return new Date(v*1000);
11960                         }
11961                         return Date.parseDate(v, dateFormat);
11962                     }
11963                     var parsed = Date.parse(v);
11964                     return parsed ? new Date(parsed) : null;
11965                 };
11966              break;
11967             
11968         }
11969         this.convert = cv;
11970     }
11971 };
11972
11973 Roo.data.Field.prototype = {
11974     dateFormat: null,
11975     defaultValue: "",
11976     mapping: null,
11977     sortType : null,
11978     sortDir : "ASC"
11979 };/*
11980  * Based on:
11981  * Ext JS Library 1.1.1
11982  * Copyright(c) 2006-2007, Ext JS, LLC.
11983  *
11984  * Originally Released Under LGPL - original licence link has changed is not relivant.
11985  *
11986  * Fork - LGPL
11987  * <script type="text/javascript">
11988  */
11989  
11990 // Base class for reading structured data from a data source.  This class is intended to be
11991 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11992
11993 /**
11994  * @class Roo.data.DataReader
11995  * Base class for reading structured data from a data source.  This class is intended to be
11996  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11997  */
11998
11999 Roo.data.DataReader = function(meta, recordType){
12000     
12001     this.meta = meta;
12002     
12003     this.recordType = recordType instanceof Array ? 
12004         Roo.data.Record.create(recordType) : recordType;
12005 };
12006
12007 Roo.data.DataReader.prototype = {
12008      /**
12009      * Create an empty record
12010      * @param {Object} data (optional) - overlay some values
12011      * @return {Roo.data.Record} record created.
12012      */
12013     newRow :  function(d) {
12014         var da =  {};
12015         this.recordType.prototype.fields.each(function(c) {
12016             switch( c.type) {
12017                 case 'int' : da[c.name] = 0; break;
12018                 case 'date' : da[c.name] = new Date(); break;
12019                 case 'float' : da[c.name] = 0.0; break;
12020                 case 'boolean' : da[c.name] = false; break;
12021                 default : da[c.name] = ""; break;
12022             }
12023             
12024         });
12025         return new this.recordType(Roo.apply(da, d));
12026     }
12027     
12028 };/*
12029  * Based on:
12030  * Ext JS Library 1.1.1
12031  * Copyright(c) 2006-2007, Ext JS, LLC.
12032  *
12033  * Originally Released Under LGPL - original licence link has changed is not relivant.
12034  *
12035  * Fork - LGPL
12036  * <script type="text/javascript">
12037  */
12038
12039 /**
12040  * @class Roo.data.DataProxy
12041  * @extends Roo.data.Observable
12042  * This class is an abstract base class for implementations which provide retrieval of
12043  * unformatted data objects.<br>
12044  * <p>
12045  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12046  * (of the appropriate type which knows how to parse the data object) to provide a block of
12047  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12048  * <p>
12049  * Custom implementations must implement the load method as described in
12050  * {@link Roo.data.HttpProxy#load}.
12051  */
12052 Roo.data.DataProxy = function(){
12053     this.addEvents({
12054         /**
12055          * @event beforeload
12056          * Fires before a network request is made to retrieve a data object.
12057          * @param {Object} This DataProxy object.
12058          * @param {Object} params The params parameter to the load function.
12059          */
12060         beforeload : true,
12061         /**
12062          * @event load
12063          * Fires before the load method's callback is called.
12064          * @param {Object} This DataProxy object.
12065          * @param {Object} o The data object.
12066          * @param {Object} arg The callback argument object passed to the load function.
12067          */
12068         load : true,
12069         /**
12070          * @event loadexception
12071          * Fires if an Exception occurs during data retrieval.
12072          * @param {Object} This DataProxy object.
12073          * @param {Object} o The data object.
12074          * @param {Object} arg The callback argument object passed to the load function.
12075          * @param {Object} e The Exception.
12076          */
12077         loadexception : true
12078     });
12079     Roo.data.DataProxy.superclass.constructor.call(this);
12080 };
12081
12082 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12083
12084     /**
12085      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12086      */
12087 /*
12088  * Based on:
12089  * Ext JS Library 1.1.1
12090  * Copyright(c) 2006-2007, Ext JS, LLC.
12091  *
12092  * Originally Released Under LGPL - original licence link has changed is not relivant.
12093  *
12094  * Fork - LGPL
12095  * <script type="text/javascript">
12096  */
12097 /**
12098  * @class Roo.data.MemoryProxy
12099  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12100  * to the Reader when its load method is called.
12101  * @constructor
12102  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12103  */
12104 Roo.data.MemoryProxy = function(data){
12105     if (data.data) {
12106         data = data.data;
12107     }
12108     Roo.data.MemoryProxy.superclass.constructor.call(this);
12109     this.data = data;
12110 };
12111
12112 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12113     
12114     /**
12115      * Load data from the requested source (in this case an in-memory
12116      * data object passed to the constructor), read the data object into
12117      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12118      * process that block using the passed callback.
12119      * @param {Object} params This parameter is not used by the MemoryProxy class.
12120      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12121      * object into a block of Roo.data.Records.
12122      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12123      * The function must be passed <ul>
12124      * <li>The Record block object</li>
12125      * <li>The "arg" argument from the load function</li>
12126      * <li>A boolean success indicator</li>
12127      * </ul>
12128      * @param {Object} scope The scope in which to call the callback
12129      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12130      */
12131     load : function(params, reader, callback, scope, arg){
12132         params = params || {};
12133         var result;
12134         try {
12135             result = reader.readRecords(this.data);
12136         }catch(e){
12137             this.fireEvent("loadexception", this, arg, null, e);
12138             callback.call(scope, null, arg, false);
12139             return;
12140         }
12141         callback.call(scope, result, arg, true);
12142     },
12143     
12144     // private
12145     update : function(params, records){
12146         
12147     }
12148 });/*
12149  * Based on:
12150  * Ext JS Library 1.1.1
12151  * Copyright(c) 2006-2007, Ext JS, LLC.
12152  *
12153  * Originally Released Under LGPL - original licence link has changed is not relivant.
12154  *
12155  * Fork - LGPL
12156  * <script type="text/javascript">
12157  */
12158 /**
12159  * @class Roo.data.HttpProxy
12160  * @extends Roo.data.DataProxy
12161  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12162  * configured to reference a certain URL.<br><br>
12163  * <p>
12164  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12165  * from which the running page was served.<br><br>
12166  * <p>
12167  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12168  * <p>
12169  * Be aware that to enable the browser to parse an XML document, the server must set
12170  * the Content-Type header in the HTTP response to "text/xml".
12171  * @constructor
12172  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12173  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12174  * will be used to make the request.
12175  */
12176 Roo.data.HttpProxy = function(conn){
12177     Roo.data.HttpProxy.superclass.constructor.call(this);
12178     // is conn a conn config or a real conn?
12179     this.conn = conn;
12180     this.useAjax = !conn || !conn.events;
12181   
12182 };
12183
12184 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12185     // thse are take from connection...
12186     
12187     /**
12188      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12189      */
12190     /**
12191      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12192      * extra parameters to each request made by this object. (defaults to undefined)
12193      */
12194     /**
12195      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12196      *  to each request made by this object. (defaults to undefined)
12197      */
12198     /**
12199      * @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)
12200      */
12201     /**
12202      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12203      */
12204      /**
12205      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12206      * @type Boolean
12207      */
12208   
12209
12210     /**
12211      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12212      * @type Boolean
12213      */
12214     /**
12215      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12216      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12217      * a finer-grained basis than the DataProxy events.
12218      */
12219     getConnection : function(){
12220         return this.useAjax ? Roo.Ajax : this.conn;
12221     },
12222
12223     /**
12224      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12225      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12226      * process that block using the passed callback.
12227      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12228      * for the request to the remote server.
12229      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12230      * object into a block of Roo.data.Records.
12231      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12232      * The function must be passed <ul>
12233      * <li>The Record block object</li>
12234      * <li>The "arg" argument from the load function</li>
12235      * <li>A boolean success indicator</li>
12236      * </ul>
12237      * @param {Object} scope The scope in which to call the callback
12238      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12239      */
12240     load : function(params, reader, callback, scope, arg){
12241         if(this.fireEvent("beforeload", this, params) !== false){
12242             var  o = {
12243                 params : params || {},
12244                 request: {
12245                     callback : callback,
12246                     scope : scope,
12247                     arg : arg
12248                 },
12249                 reader: reader,
12250                 callback : this.loadResponse,
12251                 scope: this
12252             };
12253             if(this.useAjax){
12254                 Roo.applyIf(o, this.conn);
12255                 if(this.activeRequest){
12256                     Roo.Ajax.abort(this.activeRequest);
12257                 }
12258                 this.activeRequest = Roo.Ajax.request(o);
12259             }else{
12260                 this.conn.request(o);
12261             }
12262         }else{
12263             callback.call(scope||this, null, arg, false);
12264         }
12265     },
12266
12267     // private
12268     loadResponse : function(o, success, response){
12269         delete this.activeRequest;
12270         if(!success){
12271             this.fireEvent("loadexception", this, o, response);
12272             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12273             return;
12274         }
12275         var result;
12276         try {
12277             result = o.reader.read(response);
12278         }catch(e){
12279             this.fireEvent("loadexception", this, o, response, e);
12280             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12281             return;
12282         }
12283         
12284         this.fireEvent("load", this, o, o.request.arg);
12285         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12286     },
12287
12288     // private
12289     update : function(dataSet){
12290
12291     },
12292
12293     // private
12294     updateResponse : function(dataSet){
12295
12296     }
12297 });/*
12298  * Based on:
12299  * Ext JS Library 1.1.1
12300  * Copyright(c) 2006-2007, Ext JS, LLC.
12301  *
12302  * Originally Released Under LGPL - original licence link has changed is not relivant.
12303  *
12304  * Fork - LGPL
12305  * <script type="text/javascript">
12306  */
12307
12308 /**
12309  * @class Roo.data.ScriptTagProxy
12310  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12311  * other than the originating domain of the running page.<br><br>
12312  * <p>
12313  * <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
12314  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12315  * <p>
12316  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12317  * source code that is used as the source inside a &lt;script> tag.<br><br>
12318  * <p>
12319  * In order for the browser to process the returned data, the server must wrap the data object
12320  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12321  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12322  * depending on whether the callback name was passed:
12323  * <p>
12324  * <pre><code>
12325 boolean scriptTag = false;
12326 String cb = request.getParameter("callback");
12327 if (cb != null) {
12328     scriptTag = true;
12329     response.setContentType("text/javascript");
12330 } else {
12331     response.setContentType("application/x-json");
12332 }
12333 Writer out = response.getWriter();
12334 if (scriptTag) {
12335     out.write(cb + "(");
12336 }
12337 out.print(dataBlock.toJsonString());
12338 if (scriptTag) {
12339     out.write(");");
12340 }
12341 </pre></code>
12342  *
12343  * @constructor
12344  * @param {Object} config A configuration object.
12345  */
12346 Roo.data.ScriptTagProxy = function(config){
12347     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12348     Roo.apply(this, config);
12349     this.head = document.getElementsByTagName("head")[0];
12350 };
12351
12352 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12353
12354 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12355     /**
12356      * @cfg {String} url The URL from which to request the data object.
12357      */
12358     /**
12359      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12360      */
12361     timeout : 30000,
12362     /**
12363      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12364      * the server the name of the callback function set up by the load call to process the returned data object.
12365      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12366      * javascript output which calls this named function passing the data object as its only parameter.
12367      */
12368     callbackParam : "callback",
12369     /**
12370      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12371      * name to the request.
12372      */
12373     nocache : true,
12374
12375     /**
12376      * Load data from the configured URL, read the data object into
12377      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12378      * process that block using the passed callback.
12379      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12380      * for the request to the remote server.
12381      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12382      * object into a block of Roo.data.Records.
12383      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12384      * The function must be passed <ul>
12385      * <li>The Record block object</li>
12386      * <li>The "arg" argument from the load function</li>
12387      * <li>A boolean success indicator</li>
12388      * </ul>
12389      * @param {Object} scope The scope in which to call the callback
12390      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12391      */
12392     load : function(params, reader, callback, scope, arg){
12393         if(this.fireEvent("beforeload", this, params) !== false){
12394
12395             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12396
12397             var url = this.url;
12398             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12399             if(this.nocache){
12400                 url += "&_dc=" + (new Date().getTime());
12401             }
12402             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12403             var trans = {
12404                 id : transId,
12405                 cb : "stcCallback"+transId,
12406                 scriptId : "stcScript"+transId,
12407                 params : params,
12408                 arg : arg,
12409                 url : url,
12410                 callback : callback,
12411                 scope : scope,
12412                 reader : reader
12413             };
12414             var conn = this;
12415
12416             window[trans.cb] = function(o){
12417                 conn.handleResponse(o, trans);
12418             };
12419
12420             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12421
12422             if(this.autoAbort !== false){
12423                 this.abort();
12424             }
12425
12426             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12427
12428             var script = document.createElement("script");
12429             script.setAttribute("src", url);
12430             script.setAttribute("type", "text/javascript");
12431             script.setAttribute("id", trans.scriptId);
12432             this.head.appendChild(script);
12433
12434             this.trans = trans;
12435         }else{
12436             callback.call(scope||this, null, arg, false);
12437         }
12438     },
12439
12440     // private
12441     isLoading : function(){
12442         return this.trans ? true : false;
12443     },
12444
12445     /**
12446      * Abort the current server request.
12447      */
12448     abort : function(){
12449         if(this.isLoading()){
12450             this.destroyTrans(this.trans);
12451         }
12452     },
12453
12454     // private
12455     destroyTrans : function(trans, isLoaded){
12456         this.head.removeChild(document.getElementById(trans.scriptId));
12457         clearTimeout(trans.timeoutId);
12458         if(isLoaded){
12459             window[trans.cb] = undefined;
12460             try{
12461                 delete window[trans.cb];
12462             }catch(e){}
12463         }else{
12464             // if hasn't been loaded, wait for load to remove it to prevent script error
12465             window[trans.cb] = function(){
12466                 window[trans.cb] = undefined;
12467                 try{
12468                     delete window[trans.cb];
12469                 }catch(e){}
12470             };
12471         }
12472     },
12473
12474     // private
12475     handleResponse : function(o, trans){
12476         this.trans = false;
12477         this.destroyTrans(trans, true);
12478         var result;
12479         try {
12480             result = trans.reader.readRecords(o);
12481         }catch(e){
12482             this.fireEvent("loadexception", this, o, trans.arg, e);
12483             trans.callback.call(trans.scope||window, null, trans.arg, false);
12484             return;
12485         }
12486         this.fireEvent("load", this, o, trans.arg);
12487         trans.callback.call(trans.scope||window, result, trans.arg, true);
12488     },
12489
12490     // private
12491     handleFailure : function(trans){
12492         this.trans = false;
12493         this.destroyTrans(trans, false);
12494         this.fireEvent("loadexception", this, null, trans.arg);
12495         trans.callback.call(trans.scope||window, null, trans.arg, false);
12496     }
12497 });/*
12498  * Based on:
12499  * Ext JS Library 1.1.1
12500  * Copyright(c) 2006-2007, Ext JS, LLC.
12501  *
12502  * Originally Released Under LGPL - original licence link has changed is not relivant.
12503  *
12504  * Fork - LGPL
12505  * <script type="text/javascript">
12506  */
12507
12508 /**
12509  * @class Roo.data.JsonReader
12510  * @extends Roo.data.DataReader
12511  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12512  * based on mappings in a provided Roo.data.Record constructor.
12513  * 
12514  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12515  * in the reply previously. 
12516  * 
12517  * <p>
12518  * Example code:
12519  * <pre><code>
12520 var RecordDef = Roo.data.Record.create([
12521     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12522     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12523 ]);
12524 var myReader = new Roo.data.JsonReader({
12525     totalProperty: "results",    // The property which contains the total dataset size (optional)
12526     root: "rows",                // The property which contains an Array of row objects
12527     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12528 }, RecordDef);
12529 </code></pre>
12530  * <p>
12531  * This would consume a JSON file like this:
12532  * <pre><code>
12533 { 'results': 2, 'rows': [
12534     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12535     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12536 }
12537 </code></pre>
12538  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12539  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12540  * paged from the remote server.
12541  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12542  * @cfg {String} root name of the property which contains the Array of row objects.
12543  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12544  * @cfg {Array} fields Array of field definition objects
12545  * @constructor
12546  * Create a new JsonReader
12547  * @param {Object} meta Metadata configuration options
12548  * @param {Object} recordType Either an Array of field definition objects,
12549  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12550  */
12551 Roo.data.JsonReader = function(meta, recordType){
12552     
12553     meta = meta || {};
12554     // set some defaults:
12555     Roo.applyIf(meta, {
12556         totalProperty: 'total',
12557         successProperty : 'success',
12558         root : 'data',
12559         id : 'id'
12560     });
12561     
12562     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12563 };
12564 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12565     
12566     /**
12567      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12568      * Used by Store query builder to append _requestMeta to params.
12569      * 
12570      */
12571     metaFromRemote : false,
12572     /**
12573      * This method is only used by a DataProxy which has retrieved data from a remote server.
12574      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12575      * @return {Object} data A data block which is used by an Roo.data.Store object as
12576      * a cache of Roo.data.Records.
12577      */
12578     read : function(response){
12579         var json = response.responseText;
12580        
12581         var o = /* eval:var:o */ eval("("+json+")");
12582         if(!o) {
12583             throw {message: "JsonReader.read: Json object not found"};
12584         }
12585         
12586         if(o.metaData){
12587             
12588             delete this.ef;
12589             this.metaFromRemote = true;
12590             this.meta = o.metaData;
12591             this.recordType = Roo.data.Record.create(o.metaData.fields);
12592             this.onMetaChange(this.meta, this.recordType, o);
12593         }
12594         return this.readRecords(o);
12595     },
12596
12597     // private function a store will implement
12598     onMetaChange : function(meta, recordType, o){
12599
12600     },
12601
12602     /**
12603          * @ignore
12604          */
12605     simpleAccess: function(obj, subsc) {
12606         return obj[subsc];
12607     },
12608
12609         /**
12610          * @ignore
12611          */
12612     getJsonAccessor: function(){
12613         var re = /[\[\.]/;
12614         return function(expr) {
12615             try {
12616                 return(re.test(expr))
12617                     ? new Function("obj", "return obj." + expr)
12618                     : function(obj){
12619                         return obj[expr];
12620                     };
12621             } catch(e){}
12622             return Roo.emptyFn;
12623         };
12624     }(),
12625
12626     /**
12627      * Create a data block containing Roo.data.Records from an XML document.
12628      * @param {Object} o An object which contains an Array of row objects in the property specified
12629      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12630      * which contains the total size of the dataset.
12631      * @return {Object} data A data block which is used by an Roo.data.Store object as
12632      * a cache of Roo.data.Records.
12633      */
12634     readRecords : function(o){
12635         /**
12636          * After any data loads, the raw JSON data is available for further custom processing.
12637          * @type Object
12638          */
12639         this.o = o;
12640         var s = this.meta, Record = this.recordType,
12641             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12642
12643 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12644         if (!this.ef) {
12645             if(s.totalProperty) {
12646                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12647                 }
12648                 if(s.successProperty) {
12649                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12650                 }
12651                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12652                 if (s.id) {
12653                         var g = this.getJsonAccessor(s.id);
12654                         this.getId = function(rec) {
12655                                 var r = g(rec);  
12656                                 return (r === undefined || r === "") ? null : r;
12657                         };
12658                 } else {
12659                         this.getId = function(){return null;};
12660                 }
12661             this.ef = [];
12662             for(var jj = 0; jj < fl; jj++){
12663                 f = fi[jj];
12664                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12665                 this.ef[jj] = this.getJsonAccessor(map);
12666             }
12667         }
12668
12669         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12670         if(s.totalProperty){
12671             var vt = parseInt(this.getTotal(o), 10);
12672             if(!isNaN(vt)){
12673                 totalRecords = vt;
12674             }
12675         }
12676         if(s.successProperty){
12677             var vs = this.getSuccess(o);
12678             if(vs === false || vs === 'false'){
12679                 success = false;
12680             }
12681         }
12682         var records = [];
12683         for(var i = 0; i < c; i++){
12684                 var n = root[i];
12685             var values = {};
12686             var id = this.getId(n);
12687             for(var j = 0; j < fl; j++){
12688                 f = fi[j];
12689             var v = this.ef[j](n);
12690             if (!f.convert) {
12691                 Roo.log('missing convert for ' + f.name);
12692                 Roo.log(f);
12693                 continue;
12694             }
12695             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12696             }
12697             var record = new Record(values, id);
12698             record.json = n;
12699             records[i] = record;
12700         }
12701         return {
12702             raw : o,
12703             success : success,
12704             records : records,
12705             totalRecords : totalRecords
12706         };
12707     }
12708 });/*
12709  * Based on:
12710  * Ext JS Library 1.1.1
12711  * Copyright(c) 2006-2007, Ext JS, LLC.
12712  *
12713  * Originally Released Under LGPL - original licence link has changed is not relivant.
12714  *
12715  * Fork - LGPL
12716  * <script type="text/javascript">
12717  */
12718
12719 /**
12720  * @class Roo.data.ArrayReader
12721  * @extends Roo.data.DataReader
12722  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12723  * Each element of that Array represents a row of data fields. The
12724  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12725  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12726  * <p>
12727  * Example code:.
12728  * <pre><code>
12729 var RecordDef = Roo.data.Record.create([
12730     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12731     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12732 ]);
12733 var myReader = new Roo.data.ArrayReader({
12734     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12735 }, RecordDef);
12736 </code></pre>
12737  * <p>
12738  * This would consume an Array like this:
12739  * <pre><code>
12740 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12741   </code></pre>
12742  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12743  * @constructor
12744  * Create a new JsonReader
12745  * @param {Object} meta Metadata configuration options.
12746  * @param {Object} recordType Either an Array of field definition objects
12747  * as specified to {@link Roo.data.Record#create},
12748  * or an {@link Roo.data.Record} object
12749  * created using {@link Roo.data.Record#create}.
12750  */
12751 Roo.data.ArrayReader = function(meta, recordType){
12752     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12753 };
12754
12755 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12756     /**
12757      * Create a data block containing Roo.data.Records from an XML document.
12758      * @param {Object} o An Array of row objects which represents the dataset.
12759      * @return {Object} data A data block which is used by an Roo.data.Store object as
12760      * a cache of Roo.data.Records.
12761      */
12762     readRecords : function(o){
12763         var sid = this.meta ? this.meta.id : null;
12764         var recordType = this.recordType, fields = recordType.prototype.fields;
12765         var records = [];
12766         var root = o;
12767             for(var i = 0; i < root.length; i++){
12768                     var n = root[i];
12769                 var values = {};
12770                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12771                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12772                 var f = fields.items[j];
12773                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12774                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12775                 v = f.convert(v);
12776                 values[f.name] = v;
12777             }
12778                 var record = new recordType(values, id);
12779                 record.json = n;
12780                 records[records.length] = record;
12781             }
12782             return {
12783                 records : records,
12784                 totalRecords : records.length
12785             };
12786     }
12787 });/*
12788  * - LGPL
12789  * * 
12790  */
12791
12792 /**
12793  * @class Roo.bootstrap.ComboBox
12794  * @extends Roo.bootstrap.TriggerField
12795  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12796  * @cfg {Boolean} append (true|false) default false
12797  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12798  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12799  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12800  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12801  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12802  * @cfg {Boolean} animate default true
12803  * @cfg {Boolean} emptyResultText only for touch device
12804  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12805  * @cfg {String} emptyTitle default ''
12806  * @constructor
12807  * Create a new ComboBox.
12808  * @param {Object} config Configuration options
12809  */
12810 Roo.bootstrap.ComboBox = function(config){
12811     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12812     this.addEvents({
12813         /**
12814          * @event expand
12815          * Fires when the dropdown list is expanded
12816         * @param {Roo.bootstrap.ComboBox} combo This combo box
12817         */
12818         'expand' : true,
12819         /**
12820          * @event collapse
12821          * Fires when the dropdown list is collapsed
12822         * @param {Roo.bootstrap.ComboBox} combo This combo box
12823         */
12824         'collapse' : true,
12825         /**
12826          * @event beforeselect
12827          * Fires before a list item is selected. Return false to cancel the selection.
12828         * @param {Roo.bootstrap.ComboBox} combo This combo box
12829         * @param {Roo.data.Record} record The data record returned from the underlying store
12830         * @param {Number} index The index of the selected item in the dropdown list
12831         */
12832         'beforeselect' : true,
12833         /**
12834          * @event select
12835          * Fires when a list item is selected
12836         * @param {Roo.bootstrap.ComboBox} combo This combo box
12837         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12838         * @param {Number} index The index of the selected item in the dropdown list
12839         */
12840         'select' : true,
12841         /**
12842          * @event beforequery
12843          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12844          * The event object passed has these properties:
12845         * @param {Roo.bootstrap.ComboBox} combo This combo box
12846         * @param {String} query The query
12847         * @param {Boolean} forceAll true to force "all" query
12848         * @param {Boolean} cancel true to cancel the query
12849         * @param {Object} e The query event object
12850         */
12851         'beforequery': true,
12852          /**
12853          * @event add
12854          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12855         * @param {Roo.bootstrap.ComboBox} combo This combo box
12856         */
12857         'add' : true,
12858         /**
12859          * @event edit
12860          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12861         * @param {Roo.bootstrap.ComboBox} combo This combo box
12862         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12863         */
12864         'edit' : true,
12865         /**
12866          * @event remove
12867          * Fires when the remove value from the combobox array
12868         * @param {Roo.bootstrap.ComboBox} combo This combo box
12869         */
12870         'remove' : true,
12871         /**
12872          * @event afterremove
12873          * Fires when the remove value from the combobox array
12874         * @param {Roo.bootstrap.ComboBox} combo This combo box
12875         */
12876         'afterremove' : true,
12877         /**
12878          * @event specialfilter
12879          * Fires when specialfilter
12880             * @param {Roo.bootstrap.ComboBox} combo This combo box
12881             */
12882         'specialfilter' : true,
12883         /**
12884          * @event tick
12885          * Fires when tick the element
12886             * @param {Roo.bootstrap.ComboBox} combo This combo box
12887             */
12888         'tick' : true,
12889         /**
12890          * @event touchviewdisplay
12891          * Fires when touch view require special display (default is using displayField)
12892             * @param {Roo.bootstrap.ComboBox} combo This combo box
12893             * @param {Object} cfg set html .
12894             */
12895         'touchviewdisplay' : true
12896         
12897     });
12898     
12899     this.item = [];
12900     this.tickItems = [];
12901     
12902     this.selectedIndex = -1;
12903     if(this.mode == 'local'){
12904         if(config.queryDelay === undefined){
12905             this.queryDelay = 10;
12906         }
12907         if(config.minChars === undefined){
12908             this.minChars = 0;
12909         }
12910     }
12911 };
12912
12913 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12914      
12915     /**
12916      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12917      * rendering into an Roo.Editor, defaults to false)
12918      */
12919     /**
12920      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12921      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12922      */
12923     /**
12924      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12925      */
12926     /**
12927      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12928      * the dropdown list (defaults to undefined, with no header element)
12929      */
12930
12931      /**
12932      * @cfg {String/Roo.Template} tpl The template to use to render the output
12933      */
12934      
12935      /**
12936      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12937      */
12938     listWidth: undefined,
12939     /**
12940      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12941      * mode = 'remote' or 'text' if mode = 'local')
12942      */
12943     displayField: undefined,
12944     
12945     /**
12946      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12947      * mode = 'remote' or 'value' if mode = 'local'). 
12948      * Note: use of a valueField requires the user make a selection
12949      * in order for a value to be mapped.
12950      */
12951     valueField: undefined,
12952     /**
12953      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12954      */
12955     modalTitle : '',
12956     
12957     /**
12958      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12959      * field's data value (defaults to the underlying DOM element's name)
12960      */
12961     hiddenName: undefined,
12962     /**
12963      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12964      */
12965     listClass: '',
12966     /**
12967      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12968      */
12969     selectedClass: 'active',
12970     
12971     /**
12972      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12973      */
12974     shadow:'sides',
12975     /**
12976      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12977      * anchor positions (defaults to 'tl-bl')
12978      */
12979     listAlign: 'tl-bl?',
12980     /**
12981      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12982      */
12983     maxHeight: 300,
12984     /**
12985      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12986      * query specified by the allQuery config option (defaults to 'query')
12987      */
12988     triggerAction: 'query',
12989     /**
12990      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12991      * (defaults to 4, does not apply if editable = false)
12992      */
12993     minChars : 4,
12994     /**
12995      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12996      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12997      */
12998     typeAhead: false,
12999     /**
13000      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13001      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13002      */
13003     queryDelay: 500,
13004     /**
13005      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13006      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13007      */
13008     pageSize: 0,
13009     /**
13010      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13011      * when editable = true (defaults to false)
13012      */
13013     selectOnFocus:false,
13014     /**
13015      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13016      */
13017     queryParam: 'query',
13018     /**
13019      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13020      * when mode = 'remote' (defaults to 'Loading...')
13021      */
13022     loadingText: 'Loading...',
13023     /**
13024      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13025      */
13026     resizable: false,
13027     /**
13028      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13029      */
13030     handleHeight : 8,
13031     /**
13032      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13033      * traditional select (defaults to true)
13034      */
13035     editable: true,
13036     /**
13037      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13038      */
13039     allQuery: '',
13040     /**
13041      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13042      */
13043     mode: 'remote',
13044     /**
13045      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13046      * listWidth has a higher value)
13047      */
13048     minListWidth : 70,
13049     /**
13050      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13051      * allow the user to set arbitrary text into the field (defaults to false)
13052      */
13053     forceSelection:false,
13054     /**
13055      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13056      * if typeAhead = true (defaults to 250)
13057      */
13058     typeAheadDelay : 250,
13059     /**
13060      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13061      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13062      */
13063     valueNotFoundText : undefined,
13064     /**
13065      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13066      */
13067     blockFocus : false,
13068     
13069     /**
13070      * @cfg {Boolean} disableClear Disable showing of clear button.
13071      */
13072     disableClear : false,
13073     /**
13074      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13075      */
13076     alwaysQuery : false,
13077     
13078     /**
13079      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13080      */
13081     multiple : false,
13082     
13083     /**
13084      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13085      */
13086     invalidClass : "has-warning",
13087     
13088     /**
13089      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13090      */
13091     validClass : "has-success",
13092     
13093     /**
13094      * @cfg {Boolean} specialFilter (true|false) special filter default false
13095      */
13096     specialFilter : false,
13097     
13098     /**
13099      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13100      */
13101     mobileTouchView : true,
13102     
13103     /**
13104      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13105      */
13106     useNativeIOS : false,
13107     
13108     /**
13109      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13110      */
13111     mobile_restrict_height : false,
13112     
13113     ios_options : false,
13114     
13115     //private
13116     addicon : false,
13117     editicon: false,
13118     
13119     page: 0,
13120     hasQuery: false,
13121     append: false,
13122     loadNext: false,
13123     autoFocus : true,
13124     tickable : false,
13125     btnPosition : 'right',
13126     triggerList : true,
13127     showToggleBtn : true,
13128     animate : true,
13129     emptyResultText: 'Empty',
13130     triggerText : 'Select',
13131     emptyTitle : '',
13132     
13133     // element that contains real text value.. (when hidden is used..)
13134     
13135     getAutoCreate : function()
13136     {   
13137         var cfg = false;
13138         //render
13139         /*
13140          * Render classic select for iso
13141          */
13142         
13143         if(Roo.isIOS && this.useNativeIOS){
13144             cfg = this.getAutoCreateNativeIOS();
13145             return cfg;
13146         }
13147         
13148         /*
13149          * Touch Devices
13150          */
13151         
13152         if(Roo.isTouch && this.mobileTouchView){
13153             cfg = this.getAutoCreateTouchView();
13154             return cfg;;
13155         }
13156         
13157         /*
13158          *  Normal ComboBox
13159          */
13160         if(!this.tickable){
13161             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13162             return cfg;
13163         }
13164         
13165         /*
13166          *  ComboBox with tickable selections
13167          */
13168              
13169         var align = this.labelAlign || this.parentLabelAlign();
13170         
13171         cfg = {
13172             cls : 'form-group roo-combobox-tickable' //input-group
13173         };
13174         
13175         var btn_text_select = '';
13176         var btn_text_done = '';
13177         var btn_text_cancel = '';
13178         
13179         if (this.btn_text_show) {
13180             btn_text_select = 'Select';
13181             btn_text_done = 'Done';
13182             btn_text_cancel = 'Cancel'; 
13183         }
13184         
13185         var buttons = {
13186             tag : 'div',
13187             cls : 'tickable-buttons',
13188             cn : [
13189                 {
13190                     tag : 'button',
13191                     type : 'button',
13192                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13193                     //html : this.triggerText
13194                     html: btn_text_select
13195                 },
13196                 {
13197                     tag : 'button',
13198                     type : 'button',
13199                     name : 'ok',
13200                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13201                     //html : 'Done'
13202                     html: btn_text_done
13203                 },
13204                 {
13205                     tag : 'button',
13206                     type : 'button',
13207                     name : 'cancel',
13208                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13209                     //html : 'Cancel'
13210                     html: btn_text_cancel
13211                 }
13212             ]
13213         };
13214         
13215         if(this.editable){
13216             buttons.cn.unshift({
13217                 tag: 'input',
13218                 cls: 'roo-select2-search-field-input'
13219             });
13220         }
13221         
13222         var _this = this;
13223         
13224         Roo.each(buttons.cn, function(c){
13225             if (_this.size) {
13226                 c.cls += ' btn-' + _this.size;
13227             }
13228
13229             if (_this.disabled) {
13230                 c.disabled = true;
13231             }
13232         });
13233         
13234         var box = {
13235             tag: 'div',
13236             cn: [
13237                 {
13238                     tag: 'input',
13239                     type : 'hidden',
13240                     cls: 'form-hidden-field'
13241                 },
13242                 {
13243                     tag: 'ul',
13244                     cls: 'roo-select2-choices',
13245                     cn:[
13246                         {
13247                             tag: 'li',
13248                             cls: 'roo-select2-search-field',
13249                             cn: [
13250                                 buttons
13251                             ]
13252                         }
13253                     ]
13254                 }
13255             ]
13256         };
13257         
13258         var combobox = {
13259             cls: 'roo-select2-container input-group roo-select2-container-multi',
13260             cn: [
13261                 box
13262 //                {
13263 //                    tag: 'ul',
13264 //                    cls: 'typeahead typeahead-long dropdown-menu',
13265 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13266 //                }
13267             ]
13268         };
13269         
13270         if(this.hasFeedback && !this.allowBlank){
13271             
13272             var feedback = {
13273                 tag: 'span',
13274                 cls: 'glyphicon form-control-feedback'
13275             };
13276
13277             combobox.cn.push(feedback);
13278         }
13279         
13280         
13281         if (align ==='left' && this.fieldLabel.length) {
13282             
13283             cfg.cls += ' roo-form-group-label-left';
13284             
13285             cfg.cn = [
13286                 {
13287                     tag : 'i',
13288                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13289                     tooltip : 'This field is required'
13290                 },
13291                 {
13292                     tag: 'label',
13293                     'for' :  id,
13294                     cls : 'control-label',
13295                     html : this.fieldLabel
13296
13297                 },
13298                 {
13299                     cls : "", 
13300                     cn: [
13301                         combobox
13302                     ]
13303                 }
13304
13305             ];
13306             
13307             var labelCfg = cfg.cn[1];
13308             var contentCfg = cfg.cn[2];
13309             
13310
13311             if(this.indicatorpos == 'right'){
13312                 
13313                 cfg.cn = [
13314                     {
13315                         tag: 'label',
13316                         'for' :  id,
13317                         cls : 'control-label',
13318                         cn : [
13319                             {
13320                                 tag : 'span',
13321                                 html : this.fieldLabel
13322                             },
13323                             {
13324                                 tag : 'i',
13325                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13326                                 tooltip : 'This field is required'
13327                             }
13328                         ]
13329                     },
13330                     {
13331                         cls : "",
13332                         cn: [
13333                             combobox
13334                         ]
13335                     }
13336
13337                 ];
13338                 
13339                 
13340                 
13341                 labelCfg = cfg.cn[0];
13342                 contentCfg = cfg.cn[1];
13343             
13344             }
13345             
13346             if(this.labelWidth > 12){
13347                 labelCfg.style = "width: " + this.labelWidth + 'px';
13348             }
13349             
13350             if(this.labelWidth < 13 && this.labelmd == 0){
13351                 this.labelmd = this.labelWidth;
13352             }
13353             
13354             if(this.labellg > 0){
13355                 labelCfg.cls += ' col-lg-' + this.labellg;
13356                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13357             }
13358             
13359             if(this.labelmd > 0){
13360                 labelCfg.cls += ' col-md-' + this.labelmd;
13361                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13362             }
13363             
13364             if(this.labelsm > 0){
13365                 labelCfg.cls += ' col-sm-' + this.labelsm;
13366                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13367             }
13368             
13369             if(this.labelxs > 0){
13370                 labelCfg.cls += ' col-xs-' + this.labelxs;
13371                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13372             }
13373                 
13374                 
13375         } else if ( this.fieldLabel.length) {
13376 //                Roo.log(" label");
13377                  cfg.cn = [
13378                     {
13379                         tag : 'i',
13380                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13381                         tooltip : 'This field is required'
13382                     },
13383                     {
13384                         tag: 'label',
13385                         //cls : 'input-group-addon',
13386                         html : this.fieldLabel
13387                     },
13388                     combobox
13389                 ];
13390                 
13391                 if(this.indicatorpos == 'right'){
13392                     cfg.cn = [
13393                         {
13394                             tag: 'label',
13395                             //cls : 'input-group-addon',
13396                             html : this.fieldLabel
13397                         },
13398                         {
13399                             tag : 'i',
13400                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13401                             tooltip : 'This field is required'
13402                         },
13403                         combobox
13404                     ];
13405                     
13406                 }
13407
13408         } else {
13409             
13410 //                Roo.log(" no label && no align");
13411                 cfg = combobox
13412                      
13413                 
13414         }
13415          
13416         var settings=this;
13417         ['xs','sm','md','lg'].map(function(size){
13418             if (settings[size]) {
13419                 cfg.cls += ' col-' + size + '-' + settings[size];
13420             }
13421         });
13422         
13423         return cfg;
13424         
13425     },
13426     
13427     _initEventsCalled : false,
13428     
13429     // private
13430     initEvents: function()
13431     {   
13432         if (this._initEventsCalled) { // as we call render... prevent looping...
13433             return;
13434         }
13435         this._initEventsCalled = true;
13436         
13437         if (!this.store) {
13438             throw "can not find store for combo";
13439         }
13440         
13441         this.indicator = this.indicatorEl();
13442         
13443         this.store = Roo.factory(this.store, Roo.data);
13444         this.store.parent = this;
13445         
13446         // if we are building from html. then this element is so complex, that we can not really
13447         // use the rendered HTML.
13448         // so we have to trash and replace the previous code.
13449         if (Roo.XComponent.build_from_html) {
13450             // remove this element....
13451             var e = this.el.dom, k=0;
13452             while (e ) { e = e.previousSibling;  ++k;}
13453
13454             this.el.remove();
13455             
13456             this.el=false;
13457             this.rendered = false;
13458             
13459             this.render(this.parent().getChildContainer(true), k);
13460         }
13461         
13462         if(Roo.isIOS && this.useNativeIOS){
13463             this.initIOSView();
13464             return;
13465         }
13466         
13467         /*
13468          * Touch Devices
13469          */
13470         
13471         if(Roo.isTouch && this.mobileTouchView){
13472             this.initTouchView();
13473             return;
13474         }
13475         
13476         if(this.tickable){
13477             this.initTickableEvents();
13478             return;
13479         }
13480         
13481         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13482         
13483         if(this.hiddenName){
13484             
13485             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13486             
13487             this.hiddenField.dom.value =
13488                 this.hiddenValue !== undefined ? this.hiddenValue :
13489                 this.value !== undefined ? this.value : '';
13490
13491             // prevent input submission
13492             this.el.dom.removeAttribute('name');
13493             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13494              
13495              
13496         }
13497         //if(Roo.isGecko){
13498         //    this.el.dom.setAttribute('autocomplete', 'off');
13499         //}
13500         
13501         var cls = 'x-combo-list';
13502         
13503         //this.list = new Roo.Layer({
13504         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13505         //});
13506         
13507         var _this = this;
13508         
13509         (function(){
13510             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13511             _this.list.setWidth(lw);
13512         }).defer(100);
13513         
13514         this.list.on('mouseover', this.onViewOver, this);
13515         this.list.on('mousemove', this.onViewMove, this);
13516         this.list.on('scroll', this.onViewScroll, this);
13517         
13518         /*
13519         this.list.swallowEvent('mousewheel');
13520         this.assetHeight = 0;
13521
13522         if(this.title){
13523             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13524             this.assetHeight += this.header.getHeight();
13525         }
13526
13527         this.innerList = this.list.createChild({cls:cls+'-inner'});
13528         this.innerList.on('mouseover', this.onViewOver, this);
13529         this.innerList.on('mousemove', this.onViewMove, this);
13530         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13531         
13532         if(this.allowBlank && !this.pageSize && !this.disableClear){
13533             this.footer = this.list.createChild({cls:cls+'-ft'});
13534             this.pageTb = new Roo.Toolbar(this.footer);
13535            
13536         }
13537         if(this.pageSize){
13538             this.footer = this.list.createChild({cls:cls+'-ft'});
13539             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13540                     {pageSize: this.pageSize});
13541             
13542         }
13543         
13544         if (this.pageTb && this.allowBlank && !this.disableClear) {
13545             var _this = this;
13546             this.pageTb.add(new Roo.Toolbar.Fill(), {
13547                 cls: 'x-btn-icon x-btn-clear',
13548                 text: '&#160;',
13549                 handler: function()
13550                 {
13551                     _this.collapse();
13552                     _this.clearValue();
13553                     _this.onSelect(false, -1);
13554                 }
13555             });
13556         }
13557         if (this.footer) {
13558             this.assetHeight += this.footer.getHeight();
13559         }
13560         */
13561             
13562         if(!this.tpl){
13563             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13564         }
13565
13566         this.view = new Roo.View(this.list, this.tpl, {
13567             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13568         });
13569         //this.view.wrapEl.setDisplayed(false);
13570         this.view.on('click', this.onViewClick, this);
13571         
13572         
13573         this.store.on('beforeload', this.onBeforeLoad, this);
13574         this.store.on('load', this.onLoad, this);
13575         this.store.on('loadexception', this.onLoadException, this);
13576         /*
13577         if(this.resizable){
13578             this.resizer = new Roo.Resizable(this.list,  {
13579                pinned:true, handles:'se'
13580             });
13581             this.resizer.on('resize', function(r, w, h){
13582                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13583                 this.listWidth = w;
13584                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13585                 this.restrictHeight();
13586             }, this);
13587             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13588         }
13589         */
13590         if(!this.editable){
13591             this.editable = true;
13592             this.setEditable(false);
13593         }
13594         
13595         /*
13596         
13597         if (typeof(this.events.add.listeners) != 'undefined') {
13598             
13599             this.addicon = this.wrap.createChild(
13600                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13601        
13602             this.addicon.on('click', function(e) {
13603                 this.fireEvent('add', this);
13604             }, this);
13605         }
13606         if (typeof(this.events.edit.listeners) != 'undefined') {
13607             
13608             this.editicon = this.wrap.createChild(
13609                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13610             if (this.addicon) {
13611                 this.editicon.setStyle('margin-left', '40px');
13612             }
13613             this.editicon.on('click', function(e) {
13614                 
13615                 // we fire even  if inothing is selected..
13616                 this.fireEvent('edit', this, this.lastData );
13617                 
13618             }, this);
13619         }
13620         */
13621         
13622         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13623             "up" : function(e){
13624                 this.inKeyMode = true;
13625                 this.selectPrev();
13626             },
13627
13628             "down" : function(e){
13629                 if(!this.isExpanded()){
13630                     this.onTriggerClick();
13631                 }else{
13632                     this.inKeyMode = true;
13633                     this.selectNext();
13634                 }
13635             },
13636
13637             "enter" : function(e){
13638 //                this.onViewClick();
13639                 //return true;
13640                 this.collapse();
13641                 
13642                 if(this.fireEvent("specialkey", this, e)){
13643                     this.onViewClick(false);
13644                 }
13645                 
13646                 return true;
13647             },
13648
13649             "esc" : function(e){
13650                 this.collapse();
13651             },
13652
13653             "tab" : function(e){
13654                 this.collapse();
13655                 
13656                 if(this.fireEvent("specialkey", this, e)){
13657                     this.onViewClick(false);
13658                 }
13659                 
13660                 return true;
13661             },
13662
13663             scope : this,
13664
13665             doRelay : function(foo, bar, hname){
13666                 if(hname == 'down' || this.scope.isExpanded()){
13667                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13668                 }
13669                 return true;
13670             },
13671
13672             forceKeyDown: true
13673         });
13674         
13675         
13676         this.queryDelay = Math.max(this.queryDelay || 10,
13677                 this.mode == 'local' ? 10 : 250);
13678         
13679         
13680         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13681         
13682         if(this.typeAhead){
13683             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13684         }
13685         if(this.editable !== false){
13686             this.inputEl().on("keyup", this.onKeyUp, this);
13687         }
13688         if(this.forceSelection){
13689             this.inputEl().on('blur', this.doForce, this);
13690         }
13691         
13692         if(this.multiple){
13693             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13694             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13695         }
13696     },
13697     
13698     initTickableEvents: function()
13699     {   
13700         this.createList();
13701         
13702         if(this.hiddenName){
13703             
13704             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13705             
13706             this.hiddenField.dom.value =
13707                 this.hiddenValue !== undefined ? this.hiddenValue :
13708                 this.value !== undefined ? this.value : '';
13709
13710             // prevent input submission
13711             this.el.dom.removeAttribute('name');
13712             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13713              
13714              
13715         }
13716         
13717 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13718         
13719         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13720         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13721         if(this.triggerList){
13722             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13723         }
13724          
13725         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13726         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13727         
13728         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13729         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13730         
13731         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13732         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13733         
13734         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13735         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13736         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13737         
13738         this.okBtn.hide();
13739         this.cancelBtn.hide();
13740         
13741         var _this = this;
13742         
13743         (function(){
13744             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13745             _this.list.setWidth(lw);
13746         }).defer(100);
13747         
13748         this.list.on('mouseover', this.onViewOver, this);
13749         this.list.on('mousemove', this.onViewMove, this);
13750         
13751         this.list.on('scroll', this.onViewScroll, this);
13752         
13753         if(!this.tpl){
13754             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13755                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13756         }
13757
13758         this.view = new Roo.View(this.list, this.tpl, {
13759             singleSelect:true,
13760             tickable:true,
13761             parent:this,
13762             store: this.store,
13763             selectedClass: this.selectedClass
13764         });
13765         
13766         //this.view.wrapEl.setDisplayed(false);
13767         this.view.on('click', this.onViewClick, this);
13768         
13769         
13770         
13771         this.store.on('beforeload', this.onBeforeLoad, this);
13772         this.store.on('load', this.onLoad, this);
13773         this.store.on('loadexception', this.onLoadException, this);
13774         
13775         if(this.editable){
13776             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13777                 "up" : function(e){
13778                     this.inKeyMode = true;
13779                     this.selectPrev();
13780                 },
13781
13782                 "down" : function(e){
13783                     this.inKeyMode = true;
13784                     this.selectNext();
13785                 },
13786
13787                 "enter" : function(e){
13788                     if(this.fireEvent("specialkey", this, e)){
13789                         this.onViewClick(false);
13790                     }
13791                     
13792                     return true;
13793                 },
13794
13795                 "esc" : function(e){
13796                     this.onTickableFooterButtonClick(e, false, false);
13797                 },
13798
13799                 "tab" : function(e){
13800                     this.fireEvent("specialkey", this, e);
13801                     
13802                     this.onTickableFooterButtonClick(e, false, false);
13803                     
13804                     return true;
13805                 },
13806
13807                 scope : this,
13808
13809                 doRelay : function(e, fn, key){
13810                     if(this.scope.isExpanded()){
13811                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13812                     }
13813                     return true;
13814                 },
13815
13816                 forceKeyDown: true
13817             });
13818         }
13819         
13820         this.queryDelay = Math.max(this.queryDelay || 10,
13821                 this.mode == 'local' ? 10 : 250);
13822         
13823         
13824         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13825         
13826         if(this.typeAhead){
13827             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13828         }
13829         
13830         if(this.editable !== false){
13831             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13832         }
13833         
13834         this.indicator = this.indicatorEl();
13835         
13836         if(this.indicator){
13837             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13838             this.indicator.hide();
13839         }
13840         
13841     },
13842
13843     onDestroy : function(){
13844         if(this.view){
13845             this.view.setStore(null);
13846             this.view.el.removeAllListeners();
13847             this.view.el.remove();
13848             this.view.purgeListeners();
13849         }
13850         if(this.list){
13851             this.list.dom.innerHTML  = '';
13852         }
13853         
13854         if(this.store){
13855             this.store.un('beforeload', this.onBeforeLoad, this);
13856             this.store.un('load', this.onLoad, this);
13857             this.store.un('loadexception', this.onLoadException, this);
13858         }
13859         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13860     },
13861
13862     // private
13863     fireKey : function(e){
13864         if(e.isNavKeyPress() && !this.list.isVisible()){
13865             this.fireEvent("specialkey", this, e);
13866         }
13867     },
13868
13869     // private
13870     onResize: function(w, h){
13871 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13872 //        
13873 //        if(typeof w != 'number'){
13874 //            // we do not handle it!?!?
13875 //            return;
13876 //        }
13877 //        var tw = this.trigger.getWidth();
13878 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13879 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13880 //        var x = w - tw;
13881 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13882 //            
13883 //        //this.trigger.setStyle('left', x+'px');
13884 //        
13885 //        if(this.list && this.listWidth === undefined){
13886 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13887 //            this.list.setWidth(lw);
13888 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13889 //        }
13890         
13891     
13892         
13893     },
13894
13895     /**
13896      * Allow or prevent the user from directly editing the field text.  If false is passed,
13897      * the user will only be able to select from the items defined in the dropdown list.  This method
13898      * is the runtime equivalent of setting the 'editable' config option at config time.
13899      * @param {Boolean} value True to allow the user to directly edit the field text
13900      */
13901     setEditable : function(value){
13902         if(value == this.editable){
13903             return;
13904         }
13905         this.editable = value;
13906         if(!value){
13907             this.inputEl().dom.setAttribute('readOnly', true);
13908             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13909             this.inputEl().addClass('x-combo-noedit');
13910         }else{
13911             this.inputEl().dom.setAttribute('readOnly', false);
13912             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13913             this.inputEl().removeClass('x-combo-noedit');
13914         }
13915     },
13916
13917     // private
13918     
13919     onBeforeLoad : function(combo,opts){
13920         if(!this.hasFocus){
13921             return;
13922         }
13923          if (!opts.add) {
13924             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13925          }
13926         this.restrictHeight();
13927         this.selectedIndex = -1;
13928     },
13929
13930     // private
13931     onLoad : function(){
13932         
13933         this.hasQuery = false;
13934         
13935         if(!this.hasFocus){
13936             return;
13937         }
13938         
13939         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13940             this.loading.hide();
13941         }
13942         
13943         if(this.store.getCount() > 0){
13944             
13945             this.expand();
13946             this.restrictHeight();
13947             if(this.lastQuery == this.allQuery){
13948                 if(this.editable && !this.tickable){
13949                     this.inputEl().dom.select();
13950                 }
13951                 
13952                 if(
13953                     !this.selectByValue(this.value, true) &&
13954                     this.autoFocus && 
13955                     (
13956                         !this.store.lastOptions ||
13957                         typeof(this.store.lastOptions.add) == 'undefined' || 
13958                         this.store.lastOptions.add != true
13959                     )
13960                 ){
13961                     this.select(0, true);
13962                 }
13963             }else{
13964                 if(this.autoFocus){
13965                     this.selectNext();
13966                 }
13967                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13968                     this.taTask.delay(this.typeAheadDelay);
13969                 }
13970             }
13971         }else{
13972             this.onEmptyResults();
13973         }
13974         
13975         //this.el.focus();
13976     },
13977     // private
13978     onLoadException : function()
13979     {
13980         this.hasQuery = false;
13981         
13982         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13983             this.loading.hide();
13984         }
13985         
13986         if(this.tickable && this.editable){
13987             return;
13988         }
13989         
13990         this.collapse();
13991         // only causes errors at present
13992         //Roo.log(this.store.reader.jsonData);
13993         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13994             // fixme
13995             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13996         //}
13997         
13998         
13999     },
14000     // private
14001     onTypeAhead : function(){
14002         if(this.store.getCount() > 0){
14003             var r = this.store.getAt(0);
14004             var newValue = r.data[this.displayField];
14005             var len = newValue.length;
14006             var selStart = this.getRawValue().length;
14007             
14008             if(selStart != len){
14009                 this.setRawValue(newValue);
14010                 this.selectText(selStart, newValue.length);
14011             }
14012         }
14013     },
14014
14015     // private
14016     onSelect : function(record, index){
14017         
14018         if(this.fireEvent('beforeselect', this, record, index) !== false){
14019         
14020             this.setFromData(index > -1 ? record.data : false);
14021             
14022             this.collapse();
14023             this.fireEvent('select', this, record, index);
14024         }
14025     },
14026
14027     /**
14028      * Returns the currently selected field value or empty string if no value is set.
14029      * @return {String} value The selected value
14030      */
14031     getValue : function()
14032     {
14033         if(Roo.isIOS && this.useNativeIOS){
14034             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14035         }
14036         
14037         if(this.multiple){
14038             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14039         }
14040         
14041         if(this.valueField){
14042             return typeof this.value != 'undefined' ? this.value : '';
14043         }else{
14044             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14045         }
14046     },
14047     
14048     getRawValue : function()
14049     {
14050         if(Roo.isIOS && this.useNativeIOS){
14051             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14052         }
14053         
14054         var v = this.inputEl().getValue();
14055         
14056         return v;
14057     },
14058
14059     /**
14060      * Clears any text/value currently set in the field
14061      */
14062     clearValue : function(){
14063         
14064         if(this.hiddenField){
14065             this.hiddenField.dom.value = '';
14066         }
14067         this.value = '';
14068         this.setRawValue('');
14069         this.lastSelectionText = '';
14070         this.lastData = false;
14071         
14072         var close = this.closeTriggerEl();
14073         
14074         if(close){
14075             close.hide();
14076         }
14077         
14078         this.validate();
14079         
14080     },
14081
14082     /**
14083      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14084      * will be displayed in the field.  If the value does not match the data value of an existing item,
14085      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14086      * Otherwise the field will be blank (although the value will still be set).
14087      * @param {String} value The value to match
14088      */
14089     setValue : function(v)
14090     {
14091         if(Roo.isIOS && this.useNativeIOS){
14092             this.setIOSValue(v);
14093             return;
14094         }
14095         
14096         if(this.multiple){
14097             this.syncValue();
14098             return;
14099         }
14100         
14101         var text = v;
14102         if(this.valueField){
14103             var r = this.findRecord(this.valueField, v);
14104             if(r){
14105                 text = r.data[this.displayField];
14106             }else if(this.valueNotFoundText !== undefined){
14107                 text = this.valueNotFoundText;
14108             }
14109         }
14110         this.lastSelectionText = text;
14111         if(this.hiddenField){
14112             this.hiddenField.dom.value = v;
14113         }
14114         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14115         this.value = v;
14116         
14117         var close = this.closeTriggerEl();
14118         
14119         if(close){
14120             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14121         }
14122         
14123         this.validate();
14124     },
14125     /**
14126      * @property {Object} the last set data for the element
14127      */
14128     
14129     lastData : false,
14130     /**
14131      * Sets the value of the field based on a object which is related to the record format for the store.
14132      * @param {Object} value the value to set as. or false on reset?
14133      */
14134     setFromData : function(o){
14135         
14136         if(this.multiple){
14137             this.addItem(o);
14138             return;
14139         }
14140             
14141         var dv = ''; // display value
14142         var vv = ''; // value value..
14143         this.lastData = o;
14144         if (this.displayField) {
14145             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14146         } else {
14147             // this is an error condition!!!
14148             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14149         }
14150         
14151         if(this.valueField){
14152             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14153         }
14154         
14155         var close = this.closeTriggerEl();
14156         
14157         if(close){
14158             if(dv.length || vv * 1 > 0){
14159                 close.show() ;
14160                 this.blockFocus=true;
14161             } else {
14162                 close.hide();
14163             }             
14164         }
14165         
14166         if(this.hiddenField){
14167             this.hiddenField.dom.value = vv;
14168             
14169             this.lastSelectionText = dv;
14170             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14171             this.value = vv;
14172             return;
14173         }
14174         // no hidden field.. - we store the value in 'value', but still display
14175         // display field!!!!
14176         this.lastSelectionText = dv;
14177         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14178         this.value = vv;
14179         
14180         
14181         
14182     },
14183     // private
14184     reset : function(){
14185         // overridden so that last data is reset..
14186         
14187         if(this.multiple){
14188             this.clearItem();
14189             return;
14190         }
14191         
14192         this.setValue(this.originalValue);
14193         //this.clearInvalid();
14194         this.lastData = false;
14195         if (this.view) {
14196             this.view.clearSelections();
14197         }
14198         
14199         this.validate();
14200     },
14201     // private
14202     findRecord : function(prop, value){
14203         var record;
14204         if(this.store.getCount() > 0){
14205             this.store.each(function(r){
14206                 if(r.data[prop] == value){
14207                     record = r;
14208                     return false;
14209                 }
14210                 return true;
14211             });
14212         }
14213         return record;
14214     },
14215     
14216     getName: function()
14217     {
14218         // returns hidden if it's set..
14219         if (!this.rendered) {return ''};
14220         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14221         
14222     },
14223     // private
14224     onViewMove : function(e, t){
14225         this.inKeyMode = false;
14226     },
14227
14228     // private
14229     onViewOver : function(e, t){
14230         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14231             return;
14232         }
14233         var item = this.view.findItemFromChild(t);
14234         
14235         if(item){
14236             var index = this.view.indexOf(item);
14237             this.select(index, false);
14238         }
14239     },
14240
14241     // private
14242     onViewClick : function(view, doFocus, el, e)
14243     {
14244         var index = this.view.getSelectedIndexes()[0];
14245         
14246         var r = this.store.getAt(index);
14247         
14248         if(this.tickable){
14249             
14250             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14251                 return;
14252             }
14253             
14254             var rm = false;
14255             var _this = this;
14256             
14257             Roo.each(this.tickItems, function(v,k){
14258                 
14259                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14260                     Roo.log(v);
14261                     _this.tickItems.splice(k, 1);
14262                     
14263                     if(typeof(e) == 'undefined' && view == false){
14264                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14265                     }
14266                     
14267                     rm = true;
14268                     return;
14269                 }
14270             });
14271             
14272             if(rm){
14273                 return;
14274             }
14275             
14276             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14277                 this.tickItems.push(r.data);
14278             }
14279             
14280             if(typeof(e) == 'undefined' && view == false){
14281                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14282             }
14283                     
14284             return;
14285         }
14286         
14287         if(r){
14288             this.onSelect(r, index);
14289         }
14290         if(doFocus !== false && !this.blockFocus){
14291             this.inputEl().focus();
14292         }
14293     },
14294
14295     // private
14296     restrictHeight : function(){
14297         //this.innerList.dom.style.height = '';
14298         //var inner = this.innerList.dom;
14299         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14300         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14301         //this.list.beginUpdate();
14302         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14303         this.list.alignTo(this.inputEl(), this.listAlign);
14304         this.list.alignTo(this.inputEl(), this.listAlign);
14305         //this.list.endUpdate();
14306     },
14307
14308     // private
14309     onEmptyResults : function(){
14310         
14311         if(this.tickable && this.editable){
14312             this.hasFocus = false;
14313             this.restrictHeight();
14314             return;
14315         }
14316         
14317         this.collapse();
14318     },
14319
14320     /**
14321      * Returns true if the dropdown list is expanded, else false.
14322      */
14323     isExpanded : function(){
14324         return this.list.isVisible();
14325     },
14326
14327     /**
14328      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14329      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14330      * @param {String} value The data value of the item to select
14331      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14332      * selected item if it is not currently in view (defaults to true)
14333      * @return {Boolean} True if the value matched an item in the list, else false
14334      */
14335     selectByValue : function(v, scrollIntoView){
14336         if(v !== undefined && v !== null){
14337             var r = this.findRecord(this.valueField || this.displayField, v);
14338             if(r){
14339                 this.select(this.store.indexOf(r), scrollIntoView);
14340                 return true;
14341             }
14342         }
14343         return false;
14344     },
14345
14346     /**
14347      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14348      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14349      * @param {Number} index The zero-based index of the list item to select
14350      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14351      * selected item if it is not currently in view (defaults to true)
14352      */
14353     select : function(index, scrollIntoView){
14354         this.selectedIndex = index;
14355         this.view.select(index);
14356         if(scrollIntoView !== false){
14357             var el = this.view.getNode(index);
14358             /*
14359              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14360              */
14361             if(el){
14362                 this.list.scrollChildIntoView(el, false);
14363             }
14364         }
14365     },
14366
14367     // private
14368     selectNext : function(){
14369         var ct = this.store.getCount();
14370         if(ct > 0){
14371             if(this.selectedIndex == -1){
14372                 this.select(0);
14373             }else if(this.selectedIndex < ct-1){
14374                 this.select(this.selectedIndex+1);
14375             }
14376         }
14377     },
14378
14379     // private
14380     selectPrev : function(){
14381         var ct = this.store.getCount();
14382         if(ct > 0){
14383             if(this.selectedIndex == -1){
14384                 this.select(0);
14385             }else if(this.selectedIndex != 0){
14386                 this.select(this.selectedIndex-1);
14387             }
14388         }
14389     },
14390
14391     // private
14392     onKeyUp : function(e){
14393         if(this.editable !== false && !e.isSpecialKey()){
14394             this.lastKey = e.getKey();
14395             this.dqTask.delay(this.queryDelay);
14396         }
14397     },
14398
14399     // private
14400     validateBlur : function(){
14401         return !this.list || !this.list.isVisible();   
14402     },
14403
14404     // private
14405     initQuery : function(){
14406         
14407         var v = this.getRawValue();
14408         
14409         if(this.tickable && this.editable){
14410             v = this.tickableInputEl().getValue();
14411         }
14412         
14413         this.doQuery(v);
14414     },
14415
14416     // private
14417     doForce : function(){
14418         if(this.inputEl().dom.value.length > 0){
14419             this.inputEl().dom.value =
14420                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14421              
14422         }
14423     },
14424
14425     /**
14426      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14427      * query allowing the query action to be canceled if needed.
14428      * @param {String} query The SQL query to execute
14429      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14430      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14431      * saved in the current store (defaults to false)
14432      */
14433     doQuery : function(q, forceAll){
14434         
14435         if(q === undefined || q === null){
14436             q = '';
14437         }
14438         var qe = {
14439             query: q,
14440             forceAll: forceAll,
14441             combo: this,
14442             cancel:false
14443         };
14444         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14445             return false;
14446         }
14447         q = qe.query;
14448         
14449         forceAll = qe.forceAll;
14450         if(forceAll === true || (q.length >= this.minChars)){
14451             
14452             this.hasQuery = true;
14453             
14454             if(this.lastQuery != q || this.alwaysQuery){
14455                 this.lastQuery = q;
14456                 if(this.mode == 'local'){
14457                     this.selectedIndex = -1;
14458                     if(forceAll){
14459                         this.store.clearFilter();
14460                     }else{
14461                         
14462                         if(this.specialFilter){
14463                             this.fireEvent('specialfilter', this);
14464                             this.onLoad();
14465                             return;
14466                         }
14467                         
14468                         this.store.filter(this.displayField, q);
14469                     }
14470                     
14471                     this.store.fireEvent("datachanged", this.store);
14472                     
14473                     this.onLoad();
14474                     
14475                     
14476                 }else{
14477                     
14478                     this.store.baseParams[this.queryParam] = q;
14479                     
14480                     var options = {params : this.getParams(q)};
14481                     
14482                     if(this.loadNext){
14483                         options.add = true;
14484                         options.params.start = this.page * this.pageSize;
14485                     }
14486                     
14487                     this.store.load(options);
14488                     
14489                     /*
14490                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14491                      *  we should expand the list on onLoad
14492                      *  so command out it
14493                      */
14494 //                    this.expand();
14495                 }
14496             }else{
14497                 this.selectedIndex = -1;
14498                 this.onLoad();   
14499             }
14500         }
14501         
14502         this.loadNext = false;
14503     },
14504     
14505     // private
14506     getParams : function(q){
14507         var p = {};
14508         //p[this.queryParam] = q;
14509         
14510         if(this.pageSize){
14511             p.start = 0;
14512             p.limit = this.pageSize;
14513         }
14514         return p;
14515     },
14516
14517     /**
14518      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14519      */
14520     collapse : function(){
14521         if(!this.isExpanded()){
14522             return;
14523         }
14524         
14525         this.list.hide();
14526         
14527         this.hasFocus = false;
14528         
14529         if(this.tickable){
14530             this.okBtn.hide();
14531             this.cancelBtn.hide();
14532             this.trigger.show();
14533             
14534             if(this.editable){
14535                 this.tickableInputEl().dom.value = '';
14536                 this.tickableInputEl().blur();
14537             }
14538             
14539         }
14540         
14541         Roo.get(document).un('mousedown', this.collapseIf, this);
14542         Roo.get(document).un('mousewheel', this.collapseIf, this);
14543         if (!this.editable) {
14544             Roo.get(document).un('keydown', this.listKeyPress, this);
14545         }
14546         this.fireEvent('collapse', this);
14547         
14548         this.validate();
14549     },
14550
14551     // private
14552     collapseIf : function(e){
14553         var in_combo  = e.within(this.el);
14554         var in_list =  e.within(this.list);
14555         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14556         
14557         if (in_combo || in_list || is_list) {
14558             //e.stopPropagation();
14559             return;
14560         }
14561         
14562         if(this.tickable){
14563             this.onTickableFooterButtonClick(e, false, false);
14564         }
14565
14566         this.collapse();
14567         
14568     },
14569
14570     /**
14571      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14572      */
14573     expand : function(){
14574        
14575         if(this.isExpanded() || !this.hasFocus){
14576             return;
14577         }
14578         
14579         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14580         this.list.setWidth(lw);
14581         
14582         Roo.log('expand');
14583         
14584         this.list.show();
14585         
14586         this.restrictHeight();
14587         
14588         if(this.tickable){
14589             
14590             this.tickItems = Roo.apply([], this.item);
14591             
14592             this.okBtn.show();
14593             this.cancelBtn.show();
14594             this.trigger.hide();
14595             
14596             if(this.editable){
14597                 this.tickableInputEl().focus();
14598             }
14599             
14600         }
14601         
14602         Roo.get(document).on('mousedown', this.collapseIf, this);
14603         Roo.get(document).on('mousewheel', this.collapseIf, this);
14604         if (!this.editable) {
14605             Roo.get(document).on('keydown', this.listKeyPress, this);
14606         }
14607         
14608         this.fireEvent('expand', this);
14609     },
14610
14611     // private
14612     // Implements the default empty TriggerField.onTriggerClick function
14613     onTriggerClick : function(e)
14614     {
14615         Roo.log('trigger click');
14616         
14617         if(this.disabled || !this.triggerList){
14618             return;
14619         }
14620         
14621         this.page = 0;
14622         this.loadNext = false;
14623         
14624         if(this.isExpanded()){
14625             this.collapse();
14626             if (!this.blockFocus) {
14627                 this.inputEl().focus();
14628             }
14629             
14630         }else {
14631             this.hasFocus = true;
14632             if(this.triggerAction == 'all') {
14633                 this.doQuery(this.allQuery, true);
14634             } else {
14635                 this.doQuery(this.getRawValue());
14636             }
14637             if (!this.blockFocus) {
14638                 this.inputEl().focus();
14639             }
14640         }
14641     },
14642     
14643     onTickableTriggerClick : function(e)
14644     {
14645         if(this.disabled){
14646             return;
14647         }
14648         
14649         this.page = 0;
14650         this.loadNext = false;
14651         this.hasFocus = true;
14652         
14653         if(this.triggerAction == 'all') {
14654             this.doQuery(this.allQuery, true);
14655         } else {
14656             this.doQuery(this.getRawValue());
14657         }
14658     },
14659     
14660     onSearchFieldClick : function(e)
14661     {
14662         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14663             this.onTickableFooterButtonClick(e, false, false);
14664             return;
14665         }
14666         
14667         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14668             return;
14669         }
14670         
14671         this.page = 0;
14672         this.loadNext = false;
14673         this.hasFocus = true;
14674         
14675         if(this.triggerAction == 'all') {
14676             this.doQuery(this.allQuery, true);
14677         } else {
14678             this.doQuery(this.getRawValue());
14679         }
14680     },
14681     
14682     listKeyPress : function(e)
14683     {
14684         //Roo.log('listkeypress');
14685         // scroll to first matching element based on key pres..
14686         if (e.isSpecialKey()) {
14687             return false;
14688         }
14689         var k = String.fromCharCode(e.getKey()).toUpperCase();
14690         //Roo.log(k);
14691         var match  = false;
14692         var csel = this.view.getSelectedNodes();
14693         var cselitem = false;
14694         if (csel.length) {
14695             var ix = this.view.indexOf(csel[0]);
14696             cselitem  = this.store.getAt(ix);
14697             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14698                 cselitem = false;
14699             }
14700             
14701         }
14702         
14703         this.store.each(function(v) { 
14704             if (cselitem) {
14705                 // start at existing selection.
14706                 if (cselitem.id == v.id) {
14707                     cselitem = false;
14708                 }
14709                 return true;
14710             }
14711                 
14712             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14713                 match = this.store.indexOf(v);
14714                 return false;
14715             }
14716             return true;
14717         }, this);
14718         
14719         if (match === false) {
14720             return true; // no more action?
14721         }
14722         // scroll to?
14723         this.view.select(match);
14724         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14725         sn.scrollIntoView(sn.dom.parentNode, false);
14726     },
14727     
14728     onViewScroll : function(e, t){
14729         
14730         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){
14731             return;
14732         }
14733         
14734         this.hasQuery = true;
14735         
14736         this.loading = this.list.select('.loading', true).first();
14737         
14738         if(this.loading === null){
14739             this.list.createChild({
14740                 tag: 'div',
14741                 cls: 'loading roo-select2-more-results roo-select2-active',
14742                 html: 'Loading more results...'
14743             });
14744             
14745             this.loading = this.list.select('.loading', true).first();
14746             
14747             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14748             
14749             this.loading.hide();
14750         }
14751         
14752         this.loading.show();
14753         
14754         var _combo = this;
14755         
14756         this.page++;
14757         this.loadNext = true;
14758         
14759         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14760         
14761         return;
14762     },
14763     
14764     addItem : function(o)
14765     {   
14766         var dv = ''; // display value
14767         
14768         if (this.displayField) {
14769             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14770         } else {
14771             // this is an error condition!!!
14772             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14773         }
14774         
14775         if(!dv.length){
14776             return;
14777         }
14778         
14779         var choice = this.choices.createChild({
14780             tag: 'li',
14781             cls: 'roo-select2-search-choice',
14782             cn: [
14783                 {
14784                     tag: 'div',
14785                     html: dv
14786                 },
14787                 {
14788                     tag: 'a',
14789                     href: '#',
14790                     cls: 'roo-select2-search-choice-close fa fa-times',
14791                     tabindex: '-1'
14792                 }
14793             ]
14794             
14795         }, this.searchField);
14796         
14797         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14798         
14799         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14800         
14801         this.item.push(o);
14802         
14803         this.lastData = o;
14804         
14805         this.syncValue();
14806         
14807         this.inputEl().dom.value = '';
14808         
14809         this.validate();
14810     },
14811     
14812     onRemoveItem : function(e, _self, o)
14813     {
14814         e.preventDefault();
14815         
14816         this.lastItem = Roo.apply([], this.item);
14817         
14818         var index = this.item.indexOf(o.data) * 1;
14819         
14820         if( index < 0){
14821             Roo.log('not this item?!');
14822             return;
14823         }
14824         
14825         this.item.splice(index, 1);
14826         o.item.remove();
14827         
14828         this.syncValue();
14829         
14830         this.fireEvent('remove', this, e);
14831         
14832         this.validate();
14833         
14834     },
14835     
14836     syncValue : function()
14837     {
14838         if(!this.item.length){
14839             this.clearValue();
14840             return;
14841         }
14842             
14843         var value = [];
14844         var _this = this;
14845         Roo.each(this.item, function(i){
14846             if(_this.valueField){
14847                 value.push(i[_this.valueField]);
14848                 return;
14849             }
14850
14851             value.push(i);
14852         });
14853
14854         this.value = value.join(',');
14855
14856         if(this.hiddenField){
14857             this.hiddenField.dom.value = this.value;
14858         }
14859         
14860         this.store.fireEvent("datachanged", this.store);
14861         
14862         this.validate();
14863     },
14864     
14865     clearItem : function()
14866     {
14867         if(!this.multiple){
14868             return;
14869         }
14870         
14871         this.item = [];
14872         
14873         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14874            c.remove();
14875         });
14876         
14877         this.syncValue();
14878         
14879         this.validate();
14880         
14881         if(this.tickable && !Roo.isTouch){
14882             this.view.refresh();
14883         }
14884     },
14885     
14886     inputEl: function ()
14887     {
14888         if(Roo.isIOS && this.useNativeIOS){
14889             return this.el.select('select.roo-ios-select', true).first();
14890         }
14891         
14892         if(Roo.isTouch && this.mobileTouchView){
14893             return this.el.select('input.form-control',true).first();
14894         }
14895         
14896         if(this.tickable){
14897             return this.searchField;
14898         }
14899         
14900         return this.el.select('input.form-control',true).first();
14901     },
14902     
14903     onTickableFooterButtonClick : function(e, btn, el)
14904     {
14905         e.preventDefault();
14906         
14907         this.lastItem = Roo.apply([], this.item);
14908         
14909         if(btn && btn.name == 'cancel'){
14910             this.tickItems = Roo.apply([], this.item);
14911             this.collapse();
14912             return;
14913         }
14914         
14915         this.clearItem();
14916         
14917         var _this = this;
14918         
14919         Roo.each(this.tickItems, function(o){
14920             _this.addItem(o);
14921         });
14922         
14923         this.collapse();
14924         
14925     },
14926     
14927     validate : function()
14928     {
14929         if(this.getVisibilityEl().hasClass('hidden')){
14930             return true;
14931         }
14932         
14933         var v = this.getRawValue();
14934         
14935         if(this.multiple){
14936             v = this.getValue();
14937         }
14938         
14939         if(this.disabled || this.allowBlank || v.length){
14940             this.markValid();
14941             return true;
14942         }
14943         
14944         this.markInvalid();
14945         return false;
14946     },
14947     
14948     tickableInputEl : function()
14949     {
14950         if(!this.tickable || !this.editable){
14951             return this.inputEl();
14952         }
14953         
14954         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14955     },
14956     
14957     
14958     getAutoCreateTouchView : function()
14959     {
14960         var id = Roo.id();
14961         
14962         var cfg = {
14963             cls: 'form-group' //input-group
14964         };
14965         
14966         var input =  {
14967             tag: 'input',
14968             id : id,
14969             type : this.inputType,
14970             cls : 'form-control x-combo-noedit',
14971             autocomplete: 'new-password',
14972             placeholder : this.placeholder || '',
14973             readonly : true
14974         };
14975         
14976         if (this.name) {
14977             input.name = this.name;
14978         }
14979         
14980         if (this.size) {
14981             input.cls += ' input-' + this.size;
14982         }
14983         
14984         if (this.disabled) {
14985             input.disabled = true;
14986         }
14987         
14988         var inputblock = {
14989             cls : '',
14990             cn : [
14991                 input
14992             ]
14993         };
14994         
14995         if(this.before){
14996             inputblock.cls += ' input-group';
14997             
14998             inputblock.cn.unshift({
14999                 tag :'span',
15000                 cls : 'input-group-addon',
15001                 html : this.before
15002             });
15003         }
15004         
15005         if(this.removable && !this.multiple){
15006             inputblock.cls += ' roo-removable';
15007             
15008             inputblock.cn.push({
15009                 tag: 'button',
15010                 html : 'x',
15011                 cls : 'roo-combo-removable-btn close'
15012             });
15013         }
15014
15015         if(this.hasFeedback && !this.allowBlank){
15016             
15017             inputblock.cls += ' has-feedback';
15018             
15019             inputblock.cn.push({
15020                 tag: 'span',
15021                 cls: 'glyphicon form-control-feedback'
15022             });
15023             
15024         }
15025         
15026         if (this.after) {
15027             
15028             inputblock.cls += (this.before) ? '' : ' input-group';
15029             
15030             inputblock.cn.push({
15031                 tag :'span',
15032                 cls : 'input-group-addon',
15033                 html : this.after
15034             });
15035         }
15036
15037         var box = {
15038             tag: 'div',
15039             cn: [
15040                 {
15041                     tag: 'input',
15042                     type : 'hidden',
15043                     cls: 'form-hidden-field'
15044                 },
15045                 inputblock
15046             ]
15047             
15048         };
15049         
15050         if(this.multiple){
15051             box = {
15052                 tag: 'div',
15053                 cn: [
15054                     {
15055                         tag: 'input',
15056                         type : 'hidden',
15057                         cls: 'form-hidden-field'
15058                     },
15059                     {
15060                         tag: 'ul',
15061                         cls: 'roo-select2-choices',
15062                         cn:[
15063                             {
15064                                 tag: 'li',
15065                                 cls: 'roo-select2-search-field',
15066                                 cn: [
15067
15068                                     inputblock
15069                                 ]
15070                             }
15071                         ]
15072                     }
15073                 ]
15074             }
15075         };
15076         
15077         var combobox = {
15078             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15079             cn: [
15080                 box
15081             ]
15082         };
15083         
15084         if(!this.multiple && this.showToggleBtn){
15085             
15086             var caret = {
15087                         tag: 'span',
15088                         cls: 'caret'
15089             };
15090             
15091             if (this.caret != false) {
15092                 caret = {
15093                      tag: 'i',
15094                      cls: 'fa fa-' + this.caret
15095                 };
15096                 
15097             }
15098             
15099             combobox.cn.push({
15100                 tag :'span',
15101                 cls : 'input-group-addon btn dropdown-toggle',
15102                 cn : [
15103                     caret,
15104                     {
15105                         tag: 'span',
15106                         cls: 'combobox-clear',
15107                         cn  : [
15108                             {
15109                                 tag : 'i',
15110                                 cls: 'icon-remove'
15111                             }
15112                         ]
15113                     }
15114                 ]
15115
15116             })
15117         }
15118         
15119         if(this.multiple){
15120             combobox.cls += ' roo-select2-container-multi';
15121         }
15122         
15123         var align = this.labelAlign || this.parentLabelAlign();
15124         
15125         if (align ==='left' && this.fieldLabel.length) {
15126
15127             cfg.cn = [
15128                 {
15129                    tag : 'i',
15130                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15131                    tooltip : 'This field is required'
15132                 },
15133                 {
15134                     tag: 'label',
15135                     cls : 'control-label',
15136                     html : this.fieldLabel
15137
15138                 },
15139                 {
15140                     cls : '', 
15141                     cn: [
15142                         combobox
15143                     ]
15144                 }
15145             ];
15146             
15147             var labelCfg = cfg.cn[1];
15148             var contentCfg = cfg.cn[2];
15149             
15150
15151             if(this.indicatorpos == 'right'){
15152                 cfg.cn = [
15153                     {
15154                         tag: 'label',
15155                         'for' :  id,
15156                         cls : 'control-label',
15157                         cn : [
15158                             {
15159                                 tag : 'span',
15160                                 html : this.fieldLabel
15161                             },
15162                             {
15163                                 tag : 'i',
15164                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15165                                 tooltip : 'This field is required'
15166                             }
15167                         ]
15168                     },
15169                     {
15170                         cls : "",
15171                         cn: [
15172                             combobox
15173                         ]
15174                     }
15175
15176                 ];
15177                 
15178                 labelCfg = cfg.cn[0];
15179                 contentCfg = cfg.cn[1];
15180             }
15181             
15182            
15183             
15184             if(this.labelWidth > 12){
15185                 labelCfg.style = "width: " + this.labelWidth + 'px';
15186             }
15187             
15188             if(this.labelWidth < 13 && this.labelmd == 0){
15189                 this.labelmd = this.labelWidth;
15190             }
15191             
15192             if(this.labellg > 0){
15193                 labelCfg.cls += ' col-lg-' + this.labellg;
15194                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15195             }
15196             
15197             if(this.labelmd > 0){
15198                 labelCfg.cls += ' col-md-' + this.labelmd;
15199                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15200             }
15201             
15202             if(this.labelsm > 0){
15203                 labelCfg.cls += ' col-sm-' + this.labelsm;
15204                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15205             }
15206             
15207             if(this.labelxs > 0){
15208                 labelCfg.cls += ' col-xs-' + this.labelxs;
15209                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15210             }
15211                 
15212                 
15213         } else if ( this.fieldLabel.length) {
15214             cfg.cn = [
15215                 {
15216                    tag : 'i',
15217                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15218                    tooltip : 'This field is required'
15219                 },
15220                 {
15221                     tag: 'label',
15222                     cls : 'control-label',
15223                     html : this.fieldLabel
15224
15225                 },
15226                 {
15227                     cls : '', 
15228                     cn: [
15229                         combobox
15230                     ]
15231                 }
15232             ];
15233             
15234             if(this.indicatorpos == 'right'){
15235                 cfg.cn = [
15236                     {
15237                         tag: 'label',
15238                         cls : 'control-label',
15239                         html : this.fieldLabel,
15240                         cn : [
15241                             {
15242                                tag : 'i',
15243                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15244                                tooltip : 'This field is required'
15245                             }
15246                         ]
15247                     },
15248                     {
15249                         cls : '', 
15250                         cn: [
15251                             combobox
15252                         ]
15253                     }
15254                 ];
15255             }
15256         } else {
15257             cfg.cn = combobox;    
15258         }
15259         
15260         
15261         var settings = this;
15262         
15263         ['xs','sm','md','lg'].map(function(size){
15264             if (settings[size]) {
15265                 cfg.cls += ' col-' + size + '-' + settings[size];
15266             }
15267         });
15268         
15269         return cfg;
15270     },
15271     
15272     initTouchView : function()
15273     {
15274         this.renderTouchView();
15275         
15276         this.touchViewEl.on('scroll', function(){
15277             this.el.dom.scrollTop = 0;
15278         }, this);
15279         
15280         this.originalValue = this.getValue();
15281         
15282         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15283         
15284         this.inputEl().on("click", this.showTouchView, this);
15285         if (this.triggerEl) {
15286             this.triggerEl.on("click", this.showTouchView, this);
15287         }
15288         
15289         
15290         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15291         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15292         
15293         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15294         
15295         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15296         this.store.on('load', this.onTouchViewLoad, this);
15297         this.store.on('loadexception', this.onTouchViewLoadException, this);
15298         
15299         if(this.hiddenName){
15300             
15301             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15302             
15303             this.hiddenField.dom.value =
15304                 this.hiddenValue !== undefined ? this.hiddenValue :
15305                 this.value !== undefined ? this.value : '';
15306         
15307             this.el.dom.removeAttribute('name');
15308             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15309         }
15310         
15311         if(this.multiple){
15312             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15313             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15314         }
15315         
15316         if(this.removable && !this.multiple){
15317             var close = this.closeTriggerEl();
15318             if(close){
15319                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15320                 close.on('click', this.removeBtnClick, this, close);
15321             }
15322         }
15323         /*
15324          * fix the bug in Safari iOS8
15325          */
15326         this.inputEl().on("focus", function(e){
15327             document.activeElement.blur();
15328         }, this);
15329         
15330         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15331         
15332         return;
15333         
15334         
15335     },
15336     
15337     renderTouchView : function()
15338     {
15339         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15340         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15341         
15342         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15343         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15344         
15345         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15346         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15347         this.touchViewBodyEl.setStyle('overflow', 'auto');
15348         
15349         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15350         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15351         
15352         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15353         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15354         
15355     },
15356     
15357     showTouchView : function()
15358     {
15359         if(this.disabled){
15360             return;
15361         }
15362         
15363         this.touchViewHeaderEl.hide();
15364
15365         if(this.modalTitle.length){
15366             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15367             this.touchViewHeaderEl.show();
15368         }
15369
15370         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15371         this.touchViewEl.show();
15372
15373         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15374         
15375         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15376         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15377
15378         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15379
15380         if(this.modalTitle.length){
15381             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15382         }
15383         
15384         this.touchViewBodyEl.setHeight(bodyHeight);
15385
15386         if(this.animate){
15387             var _this = this;
15388             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15389         }else{
15390             this.touchViewEl.addClass('in');
15391         }
15392         
15393         if(this._touchViewMask){
15394             Roo.get(document.body).addClass("x-body-masked");
15395             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15396             this._touchViewMask.setStyle('z-index', 10000);
15397             this._touchViewMask.addClass('show');
15398         }
15399         
15400         this.doTouchViewQuery();
15401         
15402     },
15403     
15404     hideTouchView : function()
15405     {
15406         this.touchViewEl.removeClass('in');
15407
15408         if(this.animate){
15409             var _this = this;
15410             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15411         }else{
15412             this.touchViewEl.setStyle('display', 'none');
15413         }
15414         
15415         if(this._touchViewMask){
15416             this._touchViewMask.removeClass('show');
15417             Roo.get(document.body).removeClass("x-body-masked");
15418         }
15419     },
15420     
15421     setTouchViewValue : function()
15422     {
15423         if(this.multiple){
15424             this.clearItem();
15425         
15426             var _this = this;
15427
15428             Roo.each(this.tickItems, function(o){
15429                 this.addItem(o);
15430             }, this);
15431         }
15432         
15433         this.hideTouchView();
15434     },
15435     
15436     doTouchViewQuery : function()
15437     {
15438         var qe = {
15439             query: '',
15440             forceAll: true,
15441             combo: this,
15442             cancel:false
15443         };
15444         
15445         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15446             return false;
15447         }
15448         
15449         if(!this.alwaysQuery || this.mode == 'local'){
15450             this.onTouchViewLoad();
15451             return;
15452         }
15453         
15454         this.store.load();
15455     },
15456     
15457     onTouchViewBeforeLoad : function(combo,opts)
15458     {
15459         return;
15460     },
15461
15462     // private
15463     onTouchViewLoad : function()
15464     {
15465         if(this.store.getCount() < 1){
15466             this.onTouchViewEmptyResults();
15467             return;
15468         }
15469         
15470         this.clearTouchView();
15471         
15472         var rawValue = this.getRawValue();
15473         
15474         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15475         
15476         this.tickItems = [];
15477         
15478         this.store.data.each(function(d, rowIndex){
15479             var row = this.touchViewListGroup.createChild(template);
15480             
15481             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15482                 row.addClass(d.data.cls);
15483             }
15484             
15485             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15486                 var cfg = {
15487                     data : d.data,
15488                     html : d.data[this.displayField]
15489                 };
15490                 
15491                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15492                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15493                 }
15494             }
15495             row.removeClass('selected');
15496             if(!this.multiple && this.valueField &&
15497                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15498             {
15499                 // radio buttons..
15500                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15501                 row.addClass('selected');
15502             }
15503             
15504             if(this.multiple && this.valueField &&
15505                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15506             {
15507                 
15508                 // checkboxes...
15509                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15510                 this.tickItems.push(d.data);
15511             }
15512             
15513             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15514             
15515         }, this);
15516         
15517         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15518         
15519         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15520
15521         if(this.modalTitle.length){
15522             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15523         }
15524
15525         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15526         
15527         if(this.mobile_restrict_height && listHeight < bodyHeight){
15528             this.touchViewBodyEl.setHeight(listHeight);
15529         }
15530         
15531         var _this = this;
15532         
15533         if(firstChecked && listHeight > bodyHeight){
15534             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15535         }
15536         
15537     },
15538     
15539     onTouchViewLoadException : function()
15540     {
15541         this.hideTouchView();
15542     },
15543     
15544     onTouchViewEmptyResults : function()
15545     {
15546         this.clearTouchView();
15547         
15548         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15549         
15550         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15551         
15552     },
15553     
15554     clearTouchView : function()
15555     {
15556         this.touchViewListGroup.dom.innerHTML = '';
15557     },
15558     
15559     onTouchViewClick : function(e, el, o)
15560     {
15561         e.preventDefault();
15562         
15563         var row = o.row;
15564         var rowIndex = o.rowIndex;
15565         
15566         var r = this.store.getAt(rowIndex);
15567         
15568         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15569             
15570             if(!this.multiple){
15571                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15572                     c.dom.removeAttribute('checked');
15573                 }, this);
15574
15575                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15576
15577                 this.setFromData(r.data);
15578
15579                 var close = this.closeTriggerEl();
15580
15581                 if(close){
15582                     close.show();
15583                 }
15584
15585                 this.hideTouchView();
15586
15587                 this.fireEvent('select', this, r, rowIndex);
15588
15589                 return;
15590             }
15591
15592             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15593                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15594                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15595                 return;
15596             }
15597
15598             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15599             this.addItem(r.data);
15600             this.tickItems.push(r.data);
15601         }
15602     },
15603     
15604     getAutoCreateNativeIOS : function()
15605     {
15606         var cfg = {
15607             cls: 'form-group' //input-group,
15608         };
15609         
15610         var combobox =  {
15611             tag: 'select',
15612             cls : 'roo-ios-select'
15613         };
15614         
15615         if (this.name) {
15616             combobox.name = this.name;
15617         }
15618         
15619         if (this.disabled) {
15620             combobox.disabled = true;
15621         }
15622         
15623         var settings = this;
15624         
15625         ['xs','sm','md','lg'].map(function(size){
15626             if (settings[size]) {
15627                 cfg.cls += ' col-' + size + '-' + settings[size];
15628             }
15629         });
15630         
15631         cfg.cn = combobox;
15632         
15633         return cfg;
15634         
15635     },
15636     
15637     initIOSView : function()
15638     {
15639         this.store.on('load', this.onIOSViewLoad, this);
15640         
15641         return;
15642     },
15643     
15644     onIOSViewLoad : function()
15645     {
15646         if(this.store.getCount() < 1){
15647             return;
15648         }
15649         
15650         this.clearIOSView();
15651         
15652         if(this.allowBlank) {
15653             
15654             var default_text = '-- SELECT --';
15655             
15656             if(this.placeholder.length){
15657                 default_text = this.placeholder;
15658             }
15659             
15660             if(this.emptyTitle.length){
15661                 default_text += ' - ' + this.emptyTitle + ' -';
15662             }
15663             
15664             var opt = this.inputEl().createChild({
15665                 tag: 'option',
15666                 value : 0,
15667                 html : default_text
15668             });
15669             
15670             var o = {};
15671             o[this.valueField] = 0;
15672             o[this.displayField] = default_text;
15673             
15674             this.ios_options.push({
15675                 data : o,
15676                 el : opt
15677             });
15678             
15679         }
15680         
15681         this.store.data.each(function(d, rowIndex){
15682             
15683             var html = '';
15684             
15685             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15686                 html = d.data[this.displayField];
15687             }
15688             
15689             var value = '';
15690             
15691             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15692                 value = d.data[this.valueField];
15693             }
15694             
15695             var option = {
15696                 tag: 'option',
15697                 value : value,
15698                 html : html
15699             };
15700             
15701             if(this.value == d.data[this.valueField]){
15702                 option['selected'] = true;
15703             }
15704             
15705             var opt = this.inputEl().createChild(option);
15706             
15707             this.ios_options.push({
15708                 data : d.data,
15709                 el : opt
15710             });
15711             
15712         }, this);
15713         
15714         this.inputEl().on('change', function(){
15715            this.fireEvent('select', this);
15716         }, this);
15717         
15718     },
15719     
15720     clearIOSView: function()
15721     {
15722         this.inputEl().dom.innerHTML = '';
15723         
15724         this.ios_options = [];
15725     },
15726     
15727     setIOSValue: function(v)
15728     {
15729         this.value = v;
15730         
15731         if(!this.ios_options){
15732             return;
15733         }
15734         
15735         Roo.each(this.ios_options, function(opts){
15736            
15737            opts.el.dom.removeAttribute('selected');
15738            
15739            if(opts.data[this.valueField] != v){
15740                return;
15741            }
15742            
15743            opts.el.dom.setAttribute('selected', true);
15744            
15745         }, this);
15746     }
15747
15748     /** 
15749     * @cfg {Boolean} grow 
15750     * @hide 
15751     */
15752     /** 
15753     * @cfg {Number} growMin 
15754     * @hide 
15755     */
15756     /** 
15757     * @cfg {Number} growMax 
15758     * @hide 
15759     */
15760     /**
15761      * @hide
15762      * @method autoSize
15763      */
15764 });
15765
15766 Roo.apply(Roo.bootstrap.ComboBox,  {
15767     
15768     header : {
15769         tag: 'div',
15770         cls: 'modal-header',
15771         cn: [
15772             {
15773                 tag: 'h4',
15774                 cls: 'modal-title'
15775             }
15776         ]
15777     },
15778     
15779     body : {
15780         tag: 'div',
15781         cls: 'modal-body',
15782         cn: [
15783             {
15784                 tag: 'ul',
15785                 cls: 'list-group'
15786             }
15787         ]
15788     },
15789     
15790     listItemRadio : {
15791         tag: 'li',
15792         cls: 'list-group-item',
15793         cn: [
15794             {
15795                 tag: 'span',
15796                 cls: 'roo-combobox-list-group-item-value'
15797             },
15798             {
15799                 tag: 'div',
15800                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15801                 cn: [
15802                     {
15803                         tag: 'input',
15804                         type: 'radio'
15805                     },
15806                     {
15807                         tag: 'label'
15808                     }
15809                 ]
15810             }
15811         ]
15812     },
15813     
15814     listItemCheckbox : {
15815         tag: 'li',
15816         cls: 'list-group-item',
15817         cn: [
15818             {
15819                 tag: 'span',
15820                 cls: 'roo-combobox-list-group-item-value'
15821             },
15822             {
15823                 tag: 'div',
15824                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15825                 cn: [
15826                     {
15827                         tag: 'input',
15828                         type: 'checkbox'
15829                     },
15830                     {
15831                         tag: 'label'
15832                     }
15833                 ]
15834             }
15835         ]
15836     },
15837     
15838     emptyResult : {
15839         tag: 'div',
15840         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15841     },
15842     
15843     footer : {
15844         tag: 'div',
15845         cls: 'modal-footer',
15846         cn: [
15847             {
15848                 tag: 'div',
15849                 cls: 'row',
15850                 cn: [
15851                     {
15852                         tag: 'div',
15853                         cls: 'col-xs-6 text-left',
15854                         cn: {
15855                             tag: 'button',
15856                             cls: 'btn btn-danger roo-touch-view-cancel',
15857                             html: 'Cancel'
15858                         }
15859                     },
15860                     {
15861                         tag: 'div',
15862                         cls: 'col-xs-6 text-right',
15863                         cn: {
15864                             tag: 'button',
15865                             cls: 'btn btn-success roo-touch-view-ok',
15866                             html: 'OK'
15867                         }
15868                     }
15869                 ]
15870             }
15871         ]
15872         
15873     }
15874 });
15875
15876 Roo.apply(Roo.bootstrap.ComboBox,  {
15877     
15878     touchViewTemplate : {
15879         tag: 'div',
15880         cls: 'modal fade roo-combobox-touch-view',
15881         cn: [
15882             {
15883                 tag: 'div',
15884                 cls: 'modal-dialog',
15885                 style : 'position:fixed', // we have to fix position....
15886                 cn: [
15887                     {
15888                         tag: 'div',
15889                         cls: 'modal-content',
15890                         cn: [
15891                             Roo.bootstrap.ComboBox.header,
15892                             Roo.bootstrap.ComboBox.body,
15893                             Roo.bootstrap.ComboBox.footer
15894                         ]
15895                     }
15896                 ]
15897             }
15898         ]
15899     }
15900 });/*
15901  * Based on:
15902  * Ext JS Library 1.1.1
15903  * Copyright(c) 2006-2007, Ext JS, LLC.
15904  *
15905  * Originally Released Under LGPL - original licence link has changed is not relivant.
15906  *
15907  * Fork - LGPL
15908  * <script type="text/javascript">
15909  */
15910
15911 /**
15912  * @class Roo.View
15913  * @extends Roo.util.Observable
15914  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15915  * This class also supports single and multi selection modes. <br>
15916  * Create a data model bound view:
15917  <pre><code>
15918  var store = new Roo.data.Store(...);
15919
15920  var view = new Roo.View({
15921     el : "my-element",
15922     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15923  
15924     singleSelect: true,
15925     selectedClass: "ydataview-selected",
15926     store: store
15927  });
15928
15929  // listen for node click?
15930  view.on("click", function(vw, index, node, e){
15931  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15932  });
15933
15934  // load XML data
15935  dataModel.load("foobar.xml");
15936  </code></pre>
15937  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15938  * <br><br>
15939  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15940  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15941  * 
15942  * Note: old style constructor is still suported (container, template, config)
15943  * 
15944  * @constructor
15945  * Create a new View
15946  * @param {Object} config The config object
15947  * 
15948  */
15949 Roo.View = function(config, depreciated_tpl, depreciated_config){
15950     
15951     this.parent = false;
15952     
15953     if (typeof(depreciated_tpl) == 'undefined') {
15954         // new way.. - universal constructor.
15955         Roo.apply(this, config);
15956         this.el  = Roo.get(this.el);
15957     } else {
15958         // old format..
15959         this.el  = Roo.get(config);
15960         this.tpl = depreciated_tpl;
15961         Roo.apply(this, depreciated_config);
15962     }
15963     this.wrapEl  = this.el.wrap().wrap();
15964     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15965     
15966     
15967     if(typeof(this.tpl) == "string"){
15968         this.tpl = new Roo.Template(this.tpl);
15969     } else {
15970         // support xtype ctors..
15971         this.tpl = new Roo.factory(this.tpl, Roo);
15972     }
15973     
15974     
15975     this.tpl.compile();
15976     
15977     /** @private */
15978     this.addEvents({
15979         /**
15980          * @event beforeclick
15981          * Fires before a click is processed. Returns false to cancel the default action.
15982          * @param {Roo.View} this
15983          * @param {Number} index The index of the target node
15984          * @param {HTMLElement} node The target node
15985          * @param {Roo.EventObject} e The raw event object
15986          */
15987             "beforeclick" : true,
15988         /**
15989          * @event click
15990          * Fires when a template node is clicked.
15991          * @param {Roo.View} this
15992          * @param {Number} index The index of the target node
15993          * @param {HTMLElement} node The target node
15994          * @param {Roo.EventObject} e The raw event object
15995          */
15996             "click" : true,
15997         /**
15998          * @event dblclick
15999          * Fires when a template node is double clicked.
16000          * @param {Roo.View} this
16001          * @param {Number} index The index of the target node
16002          * @param {HTMLElement} node The target node
16003          * @param {Roo.EventObject} e The raw event object
16004          */
16005             "dblclick" : true,
16006         /**
16007          * @event contextmenu
16008          * Fires when a template node is right clicked.
16009          * @param {Roo.View} this
16010          * @param {Number} index The index of the target node
16011          * @param {HTMLElement} node The target node
16012          * @param {Roo.EventObject} e The raw event object
16013          */
16014             "contextmenu" : true,
16015         /**
16016          * @event selectionchange
16017          * Fires when the selected nodes change.
16018          * @param {Roo.View} this
16019          * @param {Array} selections Array of the selected nodes
16020          */
16021             "selectionchange" : true,
16022     
16023         /**
16024          * @event beforeselect
16025          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16026          * @param {Roo.View} this
16027          * @param {HTMLElement} node The node to be selected
16028          * @param {Array} selections Array of currently selected nodes
16029          */
16030             "beforeselect" : true,
16031         /**
16032          * @event preparedata
16033          * Fires on every row to render, to allow you to change the data.
16034          * @param {Roo.View} this
16035          * @param {Object} data to be rendered (change this)
16036          */
16037           "preparedata" : true
16038           
16039           
16040         });
16041
16042
16043
16044     this.el.on({
16045         "click": this.onClick,
16046         "dblclick": this.onDblClick,
16047         "contextmenu": this.onContextMenu,
16048         scope:this
16049     });
16050
16051     this.selections = [];
16052     this.nodes = [];
16053     this.cmp = new Roo.CompositeElementLite([]);
16054     if(this.store){
16055         this.store = Roo.factory(this.store, Roo.data);
16056         this.setStore(this.store, true);
16057     }
16058     
16059     if ( this.footer && this.footer.xtype) {
16060            
16061          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16062         
16063         this.footer.dataSource = this.store;
16064         this.footer.container = fctr;
16065         this.footer = Roo.factory(this.footer, Roo);
16066         fctr.insertFirst(this.el);
16067         
16068         // this is a bit insane - as the paging toolbar seems to detach the el..
16069 //        dom.parentNode.parentNode.parentNode
16070          // they get detached?
16071     }
16072     
16073     
16074     Roo.View.superclass.constructor.call(this);
16075     
16076     
16077 };
16078
16079 Roo.extend(Roo.View, Roo.util.Observable, {
16080     
16081      /**
16082      * @cfg {Roo.data.Store} store Data store to load data from.
16083      */
16084     store : false,
16085     
16086     /**
16087      * @cfg {String|Roo.Element} el The container element.
16088      */
16089     el : '',
16090     
16091     /**
16092      * @cfg {String|Roo.Template} tpl The template used by this View 
16093      */
16094     tpl : false,
16095     /**
16096      * @cfg {String} dataName the named area of the template to use as the data area
16097      *                          Works with domtemplates roo-name="name"
16098      */
16099     dataName: false,
16100     /**
16101      * @cfg {String} selectedClass The css class to add to selected nodes
16102      */
16103     selectedClass : "x-view-selected",
16104      /**
16105      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16106      */
16107     emptyText : "",
16108     
16109     /**
16110      * @cfg {String} text to display on mask (default Loading)
16111      */
16112     mask : false,
16113     /**
16114      * @cfg {Boolean} multiSelect Allow multiple selection
16115      */
16116     multiSelect : false,
16117     /**
16118      * @cfg {Boolean} singleSelect Allow single selection
16119      */
16120     singleSelect:  false,
16121     
16122     /**
16123      * @cfg {Boolean} toggleSelect - selecting 
16124      */
16125     toggleSelect : false,
16126     
16127     /**
16128      * @cfg {Boolean} tickable - selecting 
16129      */
16130     tickable : false,
16131     
16132     /**
16133      * Returns the element this view is bound to.
16134      * @return {Roo.Element}
16135      */
16136     getEl : function(){
16137         return this.wrapEl;
16138     },
16139     
16140     
16141
16142     /**
16143      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16144      */
16145     refresh : function(){
16146         //Roo.log('refresh');
16147         var t = this.tpl;
16148         
16149         // if we are using something like 'domtemplate', then
16150         // the what gets used is:
16151         // t.applySubtemplate(NAME, data, wrapping data..)
16152         // the outer template then get' applied with
16153         //     the store 'extra data'
16154         // and the body get's added to the
16155         //      roo-name="data" node?
16156         //      <span class='roo-tpl-{name}'></span> ?????
16157         
16158         
16159         
16160         this.clearSelections();
16161         this.el.update("");
16162         var html = [];
16163         var records = this.store.getRange();
16164         if(records.length < 1) {
16165             
16166             // is this valid??  = should it render a template??
16167             
16168             this.el.update(this.emptyText);
16169             return;
16170         }
16171         var el = this.el;
16172         if (this.dataName) {
16173             this.el.update(t.apply(this.store.meta)); //????
16174             el = this.el.child('.roo-tpl-' + this.dataName);
16175         }
16176         
16177         for(var i = 0, len = records.length; i < len; i++){
16178             var data = this.prepareData(records[i].data, i, records[i]);
16179             this.fireEvent("preparedata", this, data, i, records[i]);
16180             
16181             var d = Roo.apply({}, data);
16182             
16183             if(this.tickable){
16184                 Roo.apply(d, {'roo-id' : Roo.id()});
16185                 
16186                 var _this = this;
16187             
16188                 Roo.each(this.parent.item, function(item){
16189                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16190                         return;
16191                     }
16192                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16193                 });
16194             }
16195             
16196             html[html.length] = Roo.util.Format.trim(
16197                 this.dataName ?
16198                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16199                     t.apply(d)
16200             );
16201         }
16202         
16203         
16204         
16205         el.update(html.join(""));
16206         this.nodes = el.dom.childNodes;
16207         this.updateIndexes(0);
16208     },
16209     
16210
16211     /**
16212      * Function to override to reformat the data that is sent to
16213      * the template for each node.
16214      * DEPRICATED - use the preparedata event handler.
16215      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16216      * a JSON object for an UpdateManager bound view).
16217      */
16218     prepareData : function(data, index, record)
16219     {
16220         this.fireEvent("preparedata", this, data, index, record);
16221         return data;
16222     },
16223
16224     onUpdate : function(ds, record){
16225         // Roo.log('on update');   
16226         this.clearSelections();
16227         var index = this.store.indexOf(record);
16228         var n = this.nodes[index];
16229         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16230         n.parentNode.removeChild(n);
16231         this.updateIndexes(index, index);
16232     },
16233
16234     
16235     
16236 // --------- FIXME     
16237     onAdd : function(ds, records, index)
16238     {
16239         //Roo.log(['on Add', ds, records, index] );        
16240         this.clearSelections();
16241         if(this.nodes.length == 0){
16242             this.refresh();
16243             return;
16244         }
16245         var n = this.nodes[index];
16246         for(var i = 0, len = records.length; i < len; i++){
16247             var d = this.prepareData(records[i].data, i, records[i]);
16248             if(n){
16249                 this.tpl.insertBefore(n, d);
16250             }else{
16251                 
16252                 this.tpl.append(this.el, d);
16253             }
16254         }
16255         this.updateIndexes(index);
16256     },
16257
16258     onRemove : function(ds, record, index){
16259        // Roo.log('onRemove');
16260         this.clearSelections();
16261         var el = this.dataName  ?
16262             this.el.child('.roo-tpl-' + this.dataName) :
16263             this.el; 
16264         
16265         el.dom.removeChild(this.nodes[index]);
16266         this.updateIndexes(index);
16267     },
16268
16269     /**
16270      * Refresh an individual node.
16271      * @param {Number} index
16272      */
16273     refreshNode : function(index){
16274         this.onUpdate(this.store, this.store.getAt(index));
16275     },
16276
16277     updateIndexes : function(startIndex, endIndex){
16278         var ns = this.nodes;
16279         startIndex = startIndex || 0;
16280         endIndex = endIndex || ns.length - 1;
16281         for(var i = startIndex; i <= endIndex; i++){
16282             ns[i].nodeIndex = i;
16283         }
16284     },
16285
16286     /**
16287      * Changes the data store this view uses and refresh the view.
16288      * @param {Store} store
16289      */
16290     setStore : function(store, initial){
16291         if(!initial && this.store){
16292             this.store.un("datachanged", this.refresh);
16293             this.store.un("add", this.onAdd);
16294             this.store.un("remove", this.onRemove);
16295             this.store.un("update", this.onUpdate);
16296             this.store.un("clear", this.refresh);
16297             this.store.un("beforeload", this.onBeforeLoad);
16298             this.store.un("load", this.onLoad);
16299             this.store.un("loadexception", this.onLoad);
16300         }
16301         if(store){
16302           
16303             store.on("datachanged", this.refresh, this);
16304             store.on("add", this.onAdd, this);
16305             store.on("remove", this.onRemove, this);
16306             store.on("update", this.onUpdate, this);
16307             store.on("clear", this.refresh, this);
16308             store.on("beforeload", this.onBeforeLoad, this);
16309             store.on("load", this.onLoad, this);
16310             store.on("loadexception", this.onLoad, this);
16311         }
16312         
16313         if(store){
16314             this.refresh();
16315         }
16316     },
16317     /**
16318      * onbeforeLoad - masks the loading area.
16319      *
16320      */
16321     onBeforeLoad : function(store,opts)
16322     {
16323          //Roo.log('onBeforeLoad');   
16324         if (!opts.add) {
16325             this.el.update("");
16326         }
16327         this.el.mask(this.mask ? this.mask : "Loading" ); 
16328     },
16329     onLoad : function ()
16330     {
16331         this.el.unmask();
16332     },
16333     
16334
16335     /**
16336      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16337      * @param {HTMLElement} node
16338      * @return {HTMLElement} The template node
16339      */
16340     findItemFromChild : function(node){
16341         var el = this.dataName  ?
16342             this.el.child('.roo-tpl-' + this.dataName,true) :
16343             this.el.dom; 
16344         
16345         if(!node || node.parentNode == el){
16346                     return node;
16347             }
16348             var p = node.parentNode;
16349             while(p && p != el){
16350             if(p.parentNode == el){
16351                 return p;
16352             }
16353             p = p.parentNode;
16354         }
16355             return null;
16356     },
16357
16358     /** @ignore */
16359     onClick : function(e){
16360         var item = this.findItemFromChild(e.getTarget());
16361         if(item){
16362             var index = this.indexOf(item);
16363             if(this.onItemClick(item, index, e) !== false){
16364                 this.fireEvent("click", this, index, item, e);
16365             }
16366         }else{
16367             this.clearSelections();
16368         }
16369     },
16370
16371     /** @ignore */
16372     onContextMenu : function(e){
16373         var item = this.findItemFromChild(e.getTarget());
16374         if(item){
16375             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16376         }
16377     },
16378
16379     /** @ignore */
16380     onDblClick : function(e){
16381         var item = this.findItemFromChild(e.getTarget());
16382         if(item){
16383             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16384         }
16385     },
16386
16387     onItemClick : function(item, index, e)
16388     {
16389         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16390             return false;
16391         }
16392         if (this.toggleSelect) {
16393             var m = this.isSelected(item) ? 'unselect' : 'select';
16394             //Roo.log(m);
16395             var _t = this;
16396             _t[m](item, true, false);
16397             return true;
16398         }
16399         if(this.multiSelect || this.singleSelect){
16400             if(this.multiSelect && e.shiftKey && this.lastSelection){
16401                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16402             }else{
16403                 this.select(item, this.multiSelect && e.ctrlKey);
16404                 this.lastSelection = item;
16405             }
16406             
16407             if(!this.tickable){
16408                 e.preventDefault();
16409             }
16410             
16411         }
16412         return true;
16413     },
16414
16415     /**
16416      * Get the number of selected nodes.
16417      * @return {Number}
16418      */
16419     getSelectionCount : function(){
16420         return this.selections.length;
16421     },
16422
16423     /**
16424      * Get the currently selected nodes.
16425      * @return {Array} An array of HTMLElements
16426      */
16427     getSelectedNodes : function(){
16428         return this.selections;
16429     },
16430
16431     /**
16432      * Get the indexes of the selected nodes.
16433      * @return {Array}
16434      */
16435     getSelectedIndexes : function(){
16436         var indexes = [], s = this.selections;
16437         for(var i = 0, len = s.length; i < len; i++){
16438             indexes.push(s[i].nodeIndex);
16439         }
16440         return indexes;
16441     },
16442
16443     /**
16444      * Clear all selections
16445      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16446      */
16447     clearSelections : function(suppressEvent){
16448         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16449             this.cmp.elements = this.selections;
16450             this.cmp.removeClass(this.selectedClass);
16451             this.selections = [];
16452             if(!suppressEvent){
16453                 this.fireEvent("selectionchange", this, this.selections);
16454             }
16455         }
16456     },
16457
16458     /**
16459      * Returns true if the passed node is selected
16460      * @param {HTMLElement/Number} node The node or node index
16461      * @return {Boolean}
16462      */
16463     isSelected : function(node){
16464         var s = this.selections;
16465         if(s.length < 1){
16466             return false;
16467         }
16468         node = this.getNode(node);
16469         return s.indexOf(node) !== -1;
16470     },
16471
16472     /**
16473      * Selects nodes.
16474      * @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
16475      * @param {Boolean} keepExisting (optional) true to keep existing selections
16476      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16477      */
16478     select : function(nodeInfo, keepExisting, suppressEvent){
16479         if(nodeInfo instanceof Array){
16480             if(!keepExisting){
16481                 this.clearSelections(true);
16482             }
16483             for(var i = 0, len = nodeInfo.length; i < len; i++){
16484                 this.select(nodeInfo[i], true, true);
16485             }
16486             return;
16487         } 
16488         var node = this.getNode(nodeInfo);
16489         if(!node || this.isSelected(node)){
16490             return; // already selected.
16491         }
16492         if(!keepExisting){
16493             this.clearSelections(true);
16494         }
16495         
16496         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16497             Roo.fly(node).addClass(this.selectedClass);
16498             this.selections.push(node);
16499             if(!suppressEvent){
16500                 this.fireEvent("selectionchange", this, this.selections);
16501             }
16502         }
16503         
16504         
16505     },
16506       /**
16507      * Unselects nodes.
16508      * @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
16509      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16510      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16511      */
16512     unselect : function(nodeInfo, keepExisting, suppressEvent)
16513     {
16514         if(nodeInfo instanceof Array){
16515             Roo.each(this.selections, function(s) {
16516                 this.unselect(s, nodeInfo);
16517             }, this);
16518             return;
16519         }
16520         var node = this.getNode(nodeInfo);
16521         if(!node || !this.isSelected(node)){
16522             //Roo.log("not selected");
16523             return; // not selected.
16524         }
16525         // fireevent???
16526         var ns = [];
16527         Roo.each(this.selections, function(s) {
16528             if (s == node ) {
16529                 Roo.fly(node).removeClass(this.selectedClass);
16530
16531                 return;
16532             }
16533             ns.push(s);
16534         },this);
16535         
16536         this.selections= ns;
16537         this.fireEvent("selectionchange", this, this.selections);
16538     },
16539
16540     /**
16541      * Gets a template node.
16542      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16543      * @return {HTMLElement} The node or null if it wasn't found
16544      */
16545     getNode : function(nodeInfo){
16546         if(typeof nodeInfo == "string"){
16547             return document.getElementById(nodeInfo);
16548         }else if(typeof nodeInfo == "number"){
16549             return this.nodes[nodeInfo];
16550         }
16551         return nodeInfo;
16552     },
16553
16554     /**
16555      * Gets a range template nodes.
16556      * @param {Number} startIndex
16557      * @param {Number} endIndex
16558      * @return {Array} An array of nodes
16559      */
16560     getNodes : function(start, end){
16561         var ns = this.nodes;
16562         start = start || 0;
16563         end = typeof end == "undefined" ? ns.length - 1 : end;
16564         var nodes = [];
16565         if(start <= end){
16566             for(var i = start; i <= end; i++){
16567                 nodes.push(ns[i]);
16568             }
16569         } else{
16570             for(var i = start; i >= end; i--){
16571                 nodes.push(ns[i]);
16572             }
16573         }
16574         return nodes;
16575     },
16576
16577     /**
16578      * Finds the index of the passed node
16579      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16580      * @return {Number} The index of the node or -1
16581      */
16582     indexOf : function(node){
16583         node = this.getNode(node);
16584         if(typeof node.nodeIndex == "number"){
16585             return node.nodeIndex;
16586         }
16587         var ns = this.nodes;
16588         for(var i = 0, len = ns.length; i < len; i++){
16589             if(ns[i] == node){
16590                 return i;
16591             }
16592         }
16593         return -1;
16594     }
16595 });
16596 /*
16597  * - LGPL
16598  *
16599  * based on jquery fullcalendar
16600  * 
16601  */
16602
16603 Roo.bootstrap = Roo.bootstrap || {};
16604 /**
16605  * @class Roo.bootstrap.Calendar
16606  * @extends Roo.bootstrap.Component
16607  * Bootstrap Calendar class
16608  * @cfg {Boolean} loadMask (true|false) default false
16609  * @cfg {Object} header generate the user specific header of the calendar, default false
16610
16611  * @constructor
16612  * Create a new Container
16613  * @param {Object} config The config object
16614  */
16615
16616
16617
16618 Roo.bootstrap.Calendar = function(config){
16619     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16620      this.addEvents({
16621         /**
16622              * @event select
16623              * Fires when a date is selected
16624              * @param {DatePicker} this
16625              * @param {Date} date The selected date
16626              */
16627         'select': true,
16628         /**
16629              * @event monthchange
16630              * Fires when the displayed month changes 
16631              * @param {DatePicker} this
16632              * @param {Date} date The selected month
16633              */
16634         'monthchange': true,
16635         /**
16636              * @event evententer
16637              * Fires when mouse over an event
16638              * @param {Calendar} this
16639              * @param {event} Event
16640              */
16641         'evententer': true,
16642         /**
16643              * @event eventleave
16644              * Fires when the mouse leaves an
16645              * @param {Calendar} this
16646              * @param {event}
16647              */
16648         'eventleave': true,
16649         /**
16650              * @event eventclick
16651              * Fires when the mouse click an
16652              * @param {Calendar} this
16653              * @param {event}
16654              */
16655         'eventclick': true
16656         
16657     });
16658
16659 };
16660
16661 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16662     
16663      /**
16664      * @cfg {Number} startDay
16665      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16666      */
16667     startDay : 0,
16668     
16669     loadMask : false,
16670     
16671     header : false,
16672       
16673     getAutoCreate : function(){
16674         
16675         
16676         var fc_button = function(name, corner, style, content ) {
16677             return Roo.apply({},{
16678                 tag : 'span',
16679                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16680                          (corner.length ?
16681                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16682                             ''
16683                         ),
16684                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16685                 unselectable: 'on'
16686             });
16687         };
16688         
16689         var header = {};
16690         
16691         if(!this.header){
16692             header = {
16693                 tag : 'table',
16694                 cls : 'fc-header',
16695                 style : 'width:100%',
16696                 cn : [
16697                     {
16698                         tag: 'tr',
16699                         cn : [
16700                             {
16701                                 tag : 'td',
16702                                 cls : 'fc-header-left',
16703                                 cn : [
16704                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16705                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16706                                     { tag: 'span', cls: 'fc-header-space' },
16707                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16708
16709
16710                                 ]
16711                             },
16712
16713                             {
16714                                 tag : 'td',
16715                                 cls : 'fc-header-center',
16716                                 cn : [
16717                                     {
16718                                         tag: 'span',
16719                                         cls: 'fc-header-title',
16720                                         cn : {
16721                                             tag: 'H2',
16722                                             html : 'month / year'
16723                                         }
16724                                     }
16725
16726                                 ]
16727                             },
16728                             {
16729                                 tag : 'td',
16730                                 cls : 'fc-header-right',
16731                                 cn : [
16732                               /*      fc_button('month', 'left', '', 'month' ),
16733                                     fc_button('week', '', '', 'week' ),
16734                                     fc_button('day', 'right', '', 'day' )
16735                                 */    
16736
16737                                 ]
16738                             }
16739
16740                         ]
16741                     }
16742                 ]
16743             };
16744         }
16745         
16746         header = this.header;
16747         
16748        
16749         var cal_heads = function() {
16750             var ret = [];
16751             // fixme - handle this.
16752             
16753             for (var i =0; i < Date.dayNames.length; i++) {
16754                 var d = Date.dayNames[i];
16755                 ret.push({
16756                     tag: 'th',
16757                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16758                     html : d.substring(0,3)
16759                 });
16760                 
16761             }
16762             ret[0].cls += ' fc-first';
16763             ret[6].cls += ' fc-last';
16764             return ret;
16765         };
16766         var cal_cell = function(n) {
16767             return  {
16768                 tag: 'td',
16769                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16770                 cn : [
16771                     {
16772                         cn : [
16773                             {
16774                                 cls: 'fc-day-number',
16775                                 html: 'D'
16776                             },
16777                             {
16778                                 cls: 'fc-day-content',
16779                              
16780                                 cn : [
16781                                      {
16782                                         style: 'position: relative;' // height: 17px;
16783                                     }
16784                                 ]
16785                             }
16786                             
16787                             
16788                         ]
16789                     }
16790                 ]
16791                 
16792             }
16793         };
16794         var cal_rows = function() {
16795             
16796             var ret = [];
16797             for (var r = 0; r < 6; r++) {
16798                 var row= {
16799                     tag : 'tr',
16800                     cls : 'fc-week',
16801                     cn : []
16802                 };
16803                 
16804                 for (var i =0; i < Date.dayNames.length; i++) {
16805                     var d = Date.dayNames[i];
16806                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16807
16808                 }
16809                 row.cn[0].cls+=' fc-first';
16810                 row.cn[0].cn[0].style = 'min-height:90px';
16811                 row.cn[6].cls+=' fc-last';
16812                 ret.push(row);
16813                 
16814             }
16815             ret[0].cls += ' fc-first';
16816             ret[4].cls += ' fc-prev-last';
16817             ret[5].cls += ' fc-last';
16818             return ret;
16819             
16820         };
16821         
16822         var cal_table = {
16823             tag: 'table',
16824             cls: 'fc-border-separate',
16825             style : 'width:100%',
16826             cellspacing  : 0,
16827             cn : [
16828                 { 
16829                     tag: 'thead',
16830                     cn : [
16831                         { 
16832                             tag: 'tr',
16833                             cls : 'fc-first fc-last',
16834                             cn : cal_heads()
16835                         }
16836                     ]
16837                 },
16838                 { 
16839                     tag: 'tbody',
16840                     cn : cal_rows()
16841                 }
16842                   
16843             ]
16844         };
16845          
16846          var cfg = {
16847             cls : 'fc fc-ltr',
16848             cn : [
16849                 header,
16850                 {
16851                     cls : 'fc-content',
16852                     style : "position: relative;",
16853                     cn : [
16854                         {
16855                             cls : 'fc-view fc-view-month fc-grid',
16856                             style : 'position: relative',
16857                             unselectable : 'on',
16858                             cn : [
16859                                 {
16860                                     cls : 'fc-event-container',
16861                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16862                                 },
16863                                 cal_table
16864                             ]
16865                         }
16866                     ]
16867     
16868                 }
16869            ] 
16870             
16871         };
16872         
16873          
16874         
16875         return cfg;
16876     },
16877     
16878     
16879     initEvents : function()
16880     {
16881         if(!this.store){
16882             throw "can not find store for calendar";
16883         }
16884         
16885         var mark = {
16886             tag: "div",
16887             cls:"x-dlg-mask",
16888             style: "text-align:center",
16889             cn: [
16890                 {
16891                     tag: "div",
16892                     style: "background-color:white;width:50%;margin:250 auto",
16893                     cn: [
16894                         {
16895                             tag: "img",
16896                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16897                         },
16898                         {
16899                             tag: "span",
16900                             html: "Loading"
16901                         }
16902                         
16903                     ]
16904                 }
16905             ]
16906         };
16907         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16908         
16909         var size = this.el.select('.fc-content', true).first().getSize();
16910         this.maskEl.setSize(size.width, size.height);
16911         this.maskEl.enableDisplayMode("block");
16912         if(!this.loadMask){
16913             this.maskEl.hide();
16914         }
16915         
16916         this.store = Roo.factory(this.store, Roo.data);
16917         this.store.on('load', this.onLoad, this);
16918         this.store.on('beforeload', this.onBeforeLoad, this);
16919         
16920         this.resize();
16921         
16922         this.cells = this.el.select('.fc-day',true);
16923         //Roo.log(this.cells);
16924         this.textNodes = this.el.query('.fc-day-number');
16925         this.cells.addClassOnOver('fc-state-hover');
16926         
16927         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16928         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16929         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16930         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16931         
16932         this.on('monthchange', this.onMonthChange, this);
16933         
16934         this.update(new Date().clearTime());
16935     },
16936     
16937     resize : function() {
16938         var sz  = this.el.getSize();
16939         
16940         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16941         this.el.select('.fc-day-content div',true).setHeight(34);
16942     },
16943     
16944     
16945     // private
16946     showPrevMonth : function(e){
16947         this.update(this.activeDate.add("mo", -1));
16948     },
16949     showToday : function(e){
16950         this.update(new Date().clearTime());
16951     },
16952     // private
16953     showNextMonth : function(e){
16954         this.update(this.activeDate.add("mo", 1));
16955     },
16956
16957     // private
16958     showPrevYear : function(){
16959         this.update(this.activeDate.add("y", -1));
16960     },
16961
16962     // private
16963     showNextYear : function(){
16964         this.update(this.activeDate.add("y", 1));
16965     },
16966
16967     
16968    // private
16969     update : function(date)
16970     {
16971         var vd = this.activeDate;
16972         this.activeDate = date;
16973 //        if(vd && this.el){
16974 //            var t = date.getTime();
16975 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16976 //                Roo.log('using add remove');
16977 //                
16978 //                this.fireEvent('monthchange', this, date);
16979 //                
16980 //                this.cells.removeClass("fc-state-highlight");
16981 //                this.cells.each(function(c){
16982 //                   if(c.dateValue == t){
16983 //                       c.addClass("fc-state-highlight");
16984 //                       setTimeout(function(){
16985 //                            try{c.dom.firstChild.focus();}catch(e){}
16986 //                       }, 50);
16987 //                       return false;
16988 //                   }
16989 //                   return true;
16990 //                });
16991 //                return;
16992 //            }
16993 //        }
16994         
16995         var days = date.getDaysInMonth();
16996         
16997         var firstOfMonth = date.getFirstDateOfMonth();
16998         var startingPos = firstOfMonth.getDay()-this.startDay;
16999         
17000         if(startingPos < this.startDay){
17001             startingPos += 7;
17002         }
17003         
17004         var pm = date.add(Date.MONTH, -1);
17005         var prevStart = pm.getDaysInMonth()-startingPos;
17006 //        
17007         this.cells = this.el.select('.fc-day',true);
17008         this.textNodes = this.el.query('.fc-day-number');
17009         this.cells.addClassOnOver('fc-state-hover');
17010         
17011         var cells = this.cells.elements;
17012         var textEls = this.textNodes;
17013         
17014         Roo.each(cells, function(cell){
17015             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17016         });
17017         
17018         days += startingPos;
17019
17020         // convert everything to numbers so it's fast
17021         var day = 86400000;
17022         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17023         //Roo.log(d);
17024         //Roo.log(pm);
17025         //Roo.log(prevStart);
17026         
17027         var today = new Date().clearTime().getTime();
17028         var sel = date.clearTime().getTime();
17029         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17030         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17031         var ddMatch = this.disabledDatesRE;
17032         var ddText = this.disabledDatesText;
17033         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17034         var ddaysText = this.disabledDaysText;
17035         var format = this.format;
17036         
17037         var setCellClass = function(cal, cell){
17038             cell.row = 0;
17039             cell.events = [];
17040             cell.more = [];
17041             //Roo.log('set Cell Class');
17042             cell.title = "";
17043             var t = d.getTime();
17044             
17045             //Roo.log(d);
17046             
17047             cell.dateValue = t;
17048             if(t == today){
17049                 cell.className += " fc-today";
17050                 cell.className += " fc-state-highlight";
17051                 cell.title = cal.todayText;
17052             }
17053             if(t == sel){
17054                 // disable highlight in other month..
17055                 //cell.className += " fc-state-highlight";
17056                 
17057             }
17058             // disabling
17059             if(t < min) {
17060                 cell.className = " fc-state-disabled";
17061                 cell.title = cal.minText;
17062                 return;
17063             }
17064             if(t > max) {
17065                 cell.className = " fc-state-disabled";
17066                 cell.title = cal.maxText;
17067                 return;
17068             }
17069             if(ddays){
17070                 if(ddays.indexOf(d.getDay()) != -1){
17071                     cell.title = ddaysText;
17072                     cell.className = " fc-state-disabled";
17073                 }
17074             }
17075             if(ddMatch && format){
17076                 var fvalue = d.dateFormat(format);
17077                 if(ddMatch.test(fvalue)){
17078                     cell.title = ddText.replace("%0", fvalue);
17079                     cell.className = " fc-state-disabled";
17080                 }
17081             }
17082             
17083             if (!cell.initialClassName) {
17084                 cell.initialClassName = cell.dom.className;
17085             }
17086             
17087             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17088         };
17089
17090         var i = 0;
17091         
17092         for(; i < startingPos; i++) {
17093             textEls[i].innerHTML = (++prevStart);
17094             d.setDate(d.getDate()+1);
17095             
17096             cells[i].className = "fc-past fc-other-month";
17097             setCellClass(this, cells[i]);
17098         }
17099         
17100         var intDay = 0;
17101         
17102         for(; i < days; i++){
17103             intDay = i - startingPos + 1;
17104             textEls[i].innerHTML = (intDay);
17105             d.setDate(d.getDate()+1);
17106             
17107             cells[i].className = ''; // "x-date-active";
17108             setCellClass(this, cells[i]);
17109         }
17110         var extraDays = 0;
17111         
17112         for(; i < 42; i++) {
17113             textEls[i].innerHTML = (++extraDays);
17114             d.setDate(d.getDate()+1);
17115             
17116             cells[i].className = "fc-future fc-other-month";
17117             setCellClass(this, cells[i]);
17118         }
17119         
17120         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17121         
17122         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17123         
17124         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17125         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17126         
17127         if(totalRows != 6){
17128             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17129             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17130         }
17131         
17132         this.fireEvent('monthchange', this, date);
17133         
17134         
17135         /*
17136         if(!this.internalRender){
17137             var main = this.el.dom.firstChild;
17138             var w = main.offsetWidth;
17139             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17140             Roo.fly(main).setWidth(w);
17141             this.internalRender = true;
17142             // opera does not respect the auto grow header center column
17143             // then, after it gets a width opera refuses to recalculate
17144             // without a second pass
17145             if(Roo.isOpera && !this.secondPass){
17146                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17147                 this.secondPass = true;
17148                 this.update.defer(10, this, [date]);
17149             }
17150         }
17151         */
17152         
17153     },
17154     
17155     findCell : function(dt) {
17156         dt = dt.clearTime().getTime();
17157         var ret = false;
17158         this.cells.each(function(c){
17159             //Roo.log("check " +c.dateValue + '?=' + dt);
17160             if(c.dateValue == dt){
17161                 ret = c;
17162                 return false;
17163             }
17164             return true;
17165         });
17166         
17167         return ret;
17168     },
17169     
17170     findCells : function(ev) {
17171         var s = ev.start.clone().clearTime().getTime();
17172        // Roo.log(s);
17173         var e= ev.end.clone().clearTime().getTime();
17174        // Roo.log(e);
17175         var ret = [];
17176         this.cells.each(function(c){
17177              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17178             
17179             if(c.dateValue > e){
17180                 return ;
17181             }
17182             if(c.dateValue < s){
17183                 return ;
17184             }
17185             ret.push(c);
17186         });
17187         
17188         return ret;    
17189     },
17190     
17191 //    findBestRow: function(cells)
17192 //    {
17193 //        var ret = 0;
17194 //        
17195 //        for (var i =0 ; i < cells.length;i++) {
17196 //            ret  = Math.max(cells[i].rows || 0,ret);
17197 //        }
17198 //        return ret;
17199 //        
17200 //    },
17201     
17202     
17203     addItem : function(ev)
17204     {
17205         // look for vertical location slot in
17206         var cells = this.findCells(ev);
17207         
17208 //        ev.row = this.findBestRow(cells);
17209         
17210         // work out the location.
17211         
17212         var crow = false;
17213         var rows = [];
17214         for(var i =0; i < cells.length; i++) {
17215             
17216             cells[i].row = cells[0].row;
17217             
17218             if(i == 0){
17219                 cells[i].row = cells[i].row + 1;
17220             }
17221             
17222             if (!crow) {
17223                 crow = {
17224                     start : cells[i],
17225                     end :  cells[i]
17226                 };
17227                 continue;
17228             }
17229             if (crow.start.getY() == cells[i].getY()) {
17230                 // on same row.
17231                 crow.end = cells[i];
17232                 continue;
17233             }
17234             // different row.
17235             rows.push(crow);
17236             crow = {
17237                 start: cells[i],
17238                 end : cells[i]
17239             };
17240             
17241         }
17242         
17243         rows.push(crow);
17244         ev.els = [];
17245         ev.rows = rows;
17246         ev.cells = cells;
17247         
17248         cells[0].events.push(ev);
17249         
17250         this.calevents.push(ev);
17251     },
17252     
17253     clearEvents: function() {
17254         
17255         if(!this.calevents){
17256             return;
17257         }
17258         
17259         Roo.each(this.cells.elements, function(c){
17260             c.row = 0;
17261             c.events = [];
17262             c.more = [];
17263         });
17264         
17265         Roo.each(this.calevents, function(e) {
17266             Roo.each(e.els, function(el) {
17267                 el.un('mouseenter' ,this.onEventEnter, this);
17268                 el.un('mouseleave' ,this.onEventLeave, this);
17269                 el.remove();
17270             },this);
17271         },this);
17272         
17273         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17274             e.remove();
17275         });
17276         
17277     },
17278     
17279     renderEvents: function()
17280     {   
17281         var _this = this;
17282         
17283         this.cells.each(function(c) {
17284             
17285             if(c.row < 5){
17286                 return;
17287             }
17288             
17289             var ev = c.events;
17290             
17291             var r = 4;
17292             if(c.row != c.events.length){
17293                 r = 4 - (4 - (c.row - c.events.length));
17294             }
17295             
17296             c.events = ev.slice(0, r);
17297             c.more = ev.slice(r);
17298             
17299             if(c.more.length && c.more.length == 1){
17300                 c.events.push(c.more.pop());
17301             }
17302             
17303             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17304             
17305         });
17306             
17307         this.cells.each(function(c) {
17308             
17309             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17310             
17311             
17312             for (var e = 0; e < c.events.length; e++){
17313                 var ev = c.events[e];
17314                 var rows = ev.rows;
17315                 
17316                 for(var i = 0; i < rows.length; i++) {
17317                 
17318                     // how many rows should it span..
17319
17320                     var  cfg = {
17321                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17322                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17323
17324                         unselectable : "on",
17325                         cn : [
17326                             {
17327                                 cls: 'fc-event-inner',
17328                                 cn : [
17329     //                                {
17330     //                                  tag:'span',
17331     //                                  cls: 'fc-event-time',
17332     //                                  html : cells.length > 1 ? '' : ev.time
17333     //                                },
17334                                     {
17335                                       tag:'span',
17336                                       cls: 'fc-event-title',
17337                                       html : String.format('{0}', ev.title)
17338                                     }
17339
17340
17341                                 ]
17342                             },
17343                             {
17344                                 cls: 'ui-resizable-handle ui-resizable-e',
17345                                 html : '&nbsp;&nbsp;&nbsp'
17346                             }
17347
17348                         ]
17349                     };
17350
17351                     if (i == 0) {
17352                         cfg.cls += ' fc-event-start';
17353                     }
17354                     if ((i+1) == rows.length) {
17355                         cfg.cls += ' fc-event-end';
17356                     }
17357
17358                     var ctr = _this.el.select('.fc-event-container',true).first();
17359                     var cg = ctr.createChild(cfg);
17360
17361                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17362                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17363
17364                     var r = (c.more.length) ? 1 : 0;
17365                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17366                     cg.setWidth(ebox.right - sbox.x -2);
17367
17368                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17369                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17370                     cg.on('click', _this.onEventClick, _this, ev);
17371
17372                     ev.els.push(cg);
17373                     
17374                 }
17375                 
17376             }
17377             
17378             
17379             if(c.more.length){
17380                 var  cfg = {
17381                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17382                     style : 'position: absolute',
17383                     unselectable : "on",
17384                     cn : [
17385                         {
17386                             cls: 'fc-event-inner',
17387                             cn : [
17388                                 {
17389                                   tag:'span',
17390                                   cls: 'fc-event-title',
17391                                   html : 'More'
17392                                 }
17393
17394
17395                             ]
17396                         },
17397                         {
17398                             cls: 'ui-resizable-handle ui-resizable-e',
17399                             html : '&nbsp;&nbsp;&nbsp'
17400                         }
17401
17402                     ]
17403                 };
17404
17405                 var ctr = _this.el.select('.fc-event-container',true).first();
17406                 var cg = ctr.createChild(cfg);
17407
17408                 var sbox = c.select('.fc-day-content',true).first().getBox();
17409                 var ebox = c.select('.fc-day-content',true).first().getBox();
17410                 //Roo.log(cg);
17411                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17412                 cg.setWidth(ebox.right - sbox.x -2);
17413
17414                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17415                 
17416             }
17417             
17418         });
17419         
17420         
17421         
17422     },
17423     
17424     onEventEnter: function (e, el,event,d) {
17425         this.fireEvent('evententer', this, el, event);
17426     },
17427     
17428     onEventLeave: function (e, el,event,d) {
17429         this.fireEvent('eventleave', this, el, event);
17430     },
17431     
17432     onEventClick: function (e, el,event,d) {
17433         this.fireEvent('eventclick', this, el, event);
17434     },
17435     
17436     onMonthChange: function () {
17437         this.store.load();
17438     },
17439     
17440     onMoreEventClick: function(e, el, more)
17441     {
17442         var _this = this;
17443         
17444         this.calpopover.placement = 'right';
17445         this.calpopover.setTitle('More');
17446         
17447         this.calpopover.setContent('');
17448         
17449         var ctr = this.calpopover.el.select('.popover-content', true).first();
17450         
17451         Roo.each(more, function(m){
17452             var cfg = {
17453                 cls : 'fc-event-hori fc-event-draggable',
17454                 html : m.title
17455             };
17456             var cg = ctr.createChild(cfg);
17457             
17458             cg.on('click', _this.onEventClick, _this, m);
17459         });
17460         
17461         this.calpopover.show(el);
17462         
17463         
17464     },
17465     
17466     onLoad: function () 
17467     {   
17468         this.calevents = [];
17469         var cal = this;
17470         
17471         if(this.store.getCount() > 0){
17472             this.store.data.each(function(d){
17473                cal.addItem({
17474                     id : d.data.id,
17475                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17476                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17477                     time : d.data.start_time,
17478                     title : d.data.title,
17479                     description : d.data.description,
17480                     venue : d.data.venue
17481                 });
17482             });
17483         }
17484         
17485         this.renderEvents();
17486         
17487         if(this.calevents.length && this.loadMask){
17488             this.maskEl.hide();
17489         }
17490     },
17491     
17492     onBeforeLoad: function()
17493     {
17494         this.clearEvents();
17495         if(this.loadMask){
17496             this.maskEl.show();
17497         }
17498     }
17499 });
17500
17501  
17502  /*
17503  * - LGPL
17504  *
17505  * element
17506  * 
17507  */
17508
17509 /**
17510  * @class Roo.bootstrap.Popover
17511  * @extends Roo.bootstrap.Component
17512  * Bootstrap Popover class
17513  * @cfg {String} html contents of the popover   (or false to use children..)
17514  * @cfg {String} title of popover (or false to hide)
17515  * @cfg {String} placement how it is placed
17516  * @cfg {String} trigger click || hover (or false to trigger manually)
17517  * @cfg {String} over what (parent or false to trigger manually.)
17518  * @cfg {Number} delay - delay before showing
17519  
17520  * @constructor
17521  * Create a new Popover
17522  * @param {Object} config The config object
17523  */
17524
17525 Roo.bootstrap.Popover = function(config){
17526     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17527     
17528     this.addEvents({
17529         // raw events
17530          /**
17531          * @event show
17532          * After the popover show
17533          * 
17534          * @param {Roo.bootstrap.Popover} this
17535          */
17536         "show" : true,
17537         /**
17538          * @event hide
17539          * After the popover hide
17540          * 
17541          * @param {Roo.bootstrap.Popover} this
17542          */
17543         "hide" : true
17544     });
17545 };
17546
17547 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17548     
17549     title: 'Fill in a title',
17550     html: false,
17551     
17552     placement : 'right',
17553     trigger : 'hover', // hover
17554     
17555     delay : 0,
17556     
17557     over: 'parent',
17558     
17559     can_build_overlaid : false,
17560     
17561     getChildContainer : function()
17562     {
17563         return this.el.select('.popover-content',true).first();
17564     },
17565     
17566     getAutoCreate : function(){
17567          
17568         var cfg = {
17569            cls : 'popover roo-dynamic',
17570            style: 'display:block',
17571            cn : [
17572                 {
17573                     cls : 'arrow'
17574                 },
17575                 {
17576                     cls : 'popover-inner',
17577                     cn : [
17578                         {
17579                             tag: 'h3',
17580                             cls: 'popover-title',
17581                             html : this.title
17582                         },
17583                         {
17584                             cls : 'popover-content',
17585                             html : this.html
17586                         }
17587                     ]
17588                     
17589                 }
17590            ]
17591         };
17592         
17593         return cfg;
17594     },
17595     setTitle: function(str)
17596     {
17597         this.title = str;
17598         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17599     },
17600     setContent: function(str)
17601     {
17602         this.html = str;
17603         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17604     },
17605     // as it get's added to the bottom of the page.
17606     onRender : function(ct, position)
17607     {
17608         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17609         if(!this.el){
17610             var cfg = Roo.apply({},  this.getAutoCreate());
17611             cfg.id = Roo.id();
17612             
17613             if (this.cls) {
17614                 cfg.cls += ' ' + this.cls;
17615             }
17616             if (this.style) {
17617                 cfg.style = this.style;
17618             }
17619             //Roo.log("adding to ");
17620             this.el = Roo.get(document.body).createChild(cfg, position);
17621 //            Roo.log(this.el);
17622         }
17623         this.initEvents();
17624     },
17625     
17626     initEvents : function()
17627     {
17628         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17629         this.el.enableDisplayMode('block');
17630         this.el.hide();
17631         if (this.over === false) {
17632             return; 
17633         }
17634         if (this.triggers === false) {
17635             return;
17636         }
17637         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17638         var triggers = this.trigger ? this.trigger.split(' ') : [];
17639         Roo.each(triggers, function(trigger) {
17640         
17641             if (trigger == 'click') {
17642                 on_el.on('click', this.toggle, this);
17643             } else if (trigger != 'manual') {
17644                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17645                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17646       
17647                 on_el.on(eventIn  ,this.enter, this);
17648                 on_el.on(eventOut, this.leave, this);
17649             }
17650         }, this);
17651         
17652     },
17653     
17654     
17655     // private
17656     timeout : null,
17657     hoverState : null,
17658     
17659     toggle : function () {
17660         this.hoverState == 'in' ? this.leave() : this.enter();
17661     },
17662     
17663     enter : function () {
17664         
17665         clearTimeout(this.timeout);
17666     
17667         this.hoverState = 'in';
17668     
17669         if (!this.delay || !this.delay.show) {
17670             this.show();
17671             return;
17672         }
17673         var _t = this;
17674         this.timeout = setTimeout(function () {
17675             if (_t.hoverState == 'in') {
17676                 _t.show();
17677             }
17678         }, this.delay.show)
17679     },
17680     
17681     leave : function() {
17682         clearTimeout(this.timeout);
17683     
17684         this.hoverState = 'out';
17685     
17686         if (!this.delay || !this.delay.hide) {
17687             this.hide();
17688             return;
17689         }
17690         var _t = this;
17691         this.timeout = setTimeout(function () {
17692             if (_t.hoverState == 'out') {
17693                 _t.hide();
17694             }
17695         }, this.delay.hide)
17696     },
17697     
17698     show : function (on_el)
17699     {
17700         if (!on_el) {
17701             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17702         }
17703         
17704         // set content.
17705         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17706         if (this.html !== false) {
17707             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17708         }
17709         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17710         if (!this.title.length) {
17711             this.el.select('.popover-title',true).hide();
17712         }
17713         
17714         var placement = typeof this.placement == 'function' ?
17715             this.placement.call(this, this.el, on_el) :
17716             this.placement;
17717             
17718         var autoToken = /\s?auto?\s?/i;
17719         var autoPlace = autoToken.test(placement);
17720         if (autoPlace) {
17721             placement = placement.replace(autoToken, '') || 'top';
17722         }
17723         
17724         //this.el.detach()
17725         //this.el.setXY([0,0]);
17726         this.el.show();
17727         this.el.dom.style.display='block';
17728         this.el.addClass(placement);
17729         
17730         //this.el.appendTo(on_el);
17731         
17732         var p = this.getPosition();
17733         var box = this.el.getBox();
17734         
17735         if (autoPlace) {
17736             // fixme..
17737         }
17738         var align = Roo.bootstrap.Popover.alignment[placement];
17739         
17740 //        Roo.log(align);
17741         this.el.alignTo(on_el, align[0],align[1]);
17742         //var arrow = this.el.select('.arrow',true).first();
17743         //arrow.set(align[2], 
17744         
17745         this.el.addClass('in');
17746         
17747         
17748         if (this.el.hasClass('fade')) {
17749             // fade it?
17750         }
17751         
17752         this.hoverState = 'in';
17753         
17754         this.fireEvent('show', this);
17755         
17756     },
17757     hide : function()
17758     {
17759         this.el.setXY([0,0]);
17760         this.el.removeClass('in');
17761         this.el.hide();
17762         this.hoverState = null;
17763         
17764         this.fireEvent('hide', this);
17765     }
17766     
17767 });
17768
17769 Roo.bootstrap.Popover.alignment = {
17770     'left' : ['r-l', [-10,0], 'right'],
17771     'right' : ['l-r', [10,0], 'left'],
17772     'bottom' : ['t-b', [0,10], 'top'],
17773     'top' : [ 'b-t', [0,-10], 'bottom']
17774 };
17775
17776  /*
17777  * - LGPL
17778  *
17779  * Progress
17780  * 
17781  */
17782
17783 /**
17784  * @class Roo.bootstrap.Progress
17785  * @extends Roo.bootstrap.Component
17786  * Bootstrap Progress class
17787  * @cfg {Boolean} striped striped of the progress bar
17788  * @cfg {Boolean} active animated of the progress bar
17789  * 
17790  * 
17791  * @constructor
17792  * Create a new Progress
17793  * @param {Object} config The config object
17794  */
17795
17796 Roo.bootstrap.Progress = function(config){
17797     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17798 };
17799
17800 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17801     
17802     striped : false,
17803     active: false,
17804     
17805     getAutoCreate : function(){
17806         var cfg = {
17807             tag: 'div',
17808             cls: 'progress'
17809         };
17810         
17811         
17812         if(this.striped){
17813             cfg.cls += ' progress-striped';
17814         }
17815       
17816         if(this.active){
17817             cfg.cls += ' active';
17818         }
17819         
17820         
17821         return cfg;
17822     }
17823    
17824 });
17825
17826  
17827
17828  /*
17829  * - LGPL
17830  *
17831  * ProgressBar
17832  * 
17833  */
17834
17835 /**
17836  * @class Roo.bootstrap.ProgressBar
17837  * @extends Roo.bootstrap.Component
17838  * Bootstrap ProgressBar class
17839  * @cfg {Number} aria_valuenow aria-value now
17840  * @cfg {Number} aria_valuemin aria-value min
17841  * @cfg {Number} aria_valuemax aria-value max
17842  * @cfg {String} label label for the progress bar
17843  * @cfg {String} panel (success | info | warning | danger )
17844  * @cfg {String} role role of the progress bar
17845  * @cfg {String} sr_only text
17846  * 
17847  * 
17848  * @constructor
17849  * Create a new ProgressBar
17850  * @param {Object} config The config object
17851  */
17852
17853 Roo.bootstrap.ProgressBar = function(config){
17854     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17855 };
17856
17857 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17858     
17859     aria_valuenow : 0,
17860     aria_valuemin : 0,
17861     aria_valuemax : 100,
17862     label : false,
17863     panel : false,
17864     role : false,
17865     sr_only: false,
17866     
17867     getAutoCreate : function()
17868     {
17869         
17870         var cfg = {
17871             tag: 'div',
17872             cls: 'progress-bar',
17873             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17874         };
17875         
17876         if(this.sr_only){
17877             cfg.cn = {
17878                 tag: 'span',
17879                 cls: 'sr-only',
17880                 html: this.sr_only
17881             }
17882         }
17883         
17884         if(this.role){
17885             cfg.role = this.role;
17886         }
17887         
17888         if(this.aria_valuenow){
17889             cfg['aria-valuenow'] = this.aria_valuenow;
17890         }
17891         
17892         if(this.aria_valuemin){
17893             cfg['aria-valuemin'] = this.aria_valuemin;
17894         }
17895         
17896         if(this.aria_valuemax){
17897             cfg['aria-valuemax'] = this.aria_valuemax;
17898         }
17899         
17900         if(this.label && !this.sr_only){
17901             cfg.html = this.label;
17902         }
17903         
17904         if(this.panel){
17905             cfg.cls += ' progress-bar-' + this.panel;
17906         }
17907         
17908         return cfg;
17909     },
17910     
17911     update : function(aria_valuenow)
17912     {
17913         this.aria_valuenow = aria_valuenow;
17914         
17915         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17916     }
17917    
17918 });
17919
17920  
17921
17922  /*
17923  * - LGPL
17924  *
17925  * column
17926  * 
17927  */
17928
17929 /**
17930  * @class Roo.bootstrap.TabGroup
17931  * @extends Roo.bootstrap.Column
17932  * Bootstrap Column class
17933  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17934  * @cfg {Boolean} carousel true to make the group behave like a carousel
17935  * @cfg {Boolean} bullets show bullets for the panels
17936  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17937  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17938  * @cfg {Boolean} showarrow (true|false) show arrow default true
17939  * 
17940  * @constructor
17941  * Create a new TabGroup
17942  * @param {Object} config The config object
17943  */
17944
17945 Roo.bootstrap.TabGroup = function(config){
17946     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17947     if (!this.navId) {
17948         this.navId = Roo.id();
17949     }
17950     this.tabs = [];
17951     Roo.bootstrap.TabGroup.register(this);
17952     
17953 };
17954
17955 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17956     
17957     carousel : false,
17958     transition : false,
17959     bullets : 0,
17960     timer : 0,
17961     autoslide : false,
17962     slideFn : false,
17963     slideOnTouch : false,
17964     showarrow : true,
17965     
17966     getAutoCreate : function()
17967     {
17968         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17969         
17970         cfg.cls += ' tab-content';
17971         
17972         if (this.carousel) {
17973             cfg.cls += ' carousel slide';
17974             
17975             cfg.cn = [{
17976                cls : 'carousel-inner',
17977                cn : []
17978             }];
17979         
17980             if(this.bullets  && !Roo.isTouch){
17981                 
17982                 var bullets = {
17983                     cls : 'carousel-bullets',
17984                     cn : []
17985                 };
17986                
17987                 if(this.bullets_cls){
17988                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17989                 }
17990                 
17991                 bullets.cn.push({
17992                     cls : 'clear'
17993                 });
17994                 
17995                 cfg.cn[0].cn.push(bullets);
17996             }
17997             
17998             if(this.showarrow){
17999                 cfg.cn[0].cn.push({
18000                     tag : 'div',
18001                     class : 'carousel-arrow',
18002                     cn : [
18003                         {
18004                             tag : 'div',
18005                             class : 'carousel-prev',
18006                             cn : [
18007                                 {
18008                                     tag : 'i',
18009                                     class : 'fa fa-chevron-left'
18010                                 }
18011                             ]
18012                         },
18013                         {
18014                             tag : 'div',
18015                             class : 'carousel-next',
18016                             cn : [
18017                                 {
18018                                     tag : 'i',
18019                                     class : 'fa fa-chevron-right'
18020                                 }
18021                             ]
18022                         }
18023                     ]
18024                 });
18025             }
18026             
18027         }
18028         
18029         return cfg;
18030     },
18031     
18032     initEvents:  function()
18033     {
18034 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18035 //            this.el.on("touchstart", this.onTouchStart, this);
18036 //        }
18037         
18038         if(this.autoslide){
18039             var _this = this;
18040             
18041             this.slideFn = window.setInterval(function() {
18042                 _this.showPanelNext();
18043             }, this.timer);
18044         }
18045         
18046         if(this.showarrow){
18047             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18048             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18049         }
18050         
18051         
18052     },
18053     
18054 //    onTouchStart : function(e, el, o)
18055 //    {
18056 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18057 //            return;
18058 //        }
18059 //        
18060 //        this.showPanelNext();
18061 //    },
18062     
18063     
18064     getChildContainer : function()
18065     {
18066         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18067     },
18068     
18069     /**
18070     * register a Navigation item
18071     * @param {Roo.bootstrap.NavItem} the navitem to add
18072     */
18073     register : function(item)
18074     {
18075         this.tabs.push( item);
18076         item.navId = this.navId; // not really needed..
18077         this.addBullet();
18078     
18079     },
18080     
18081     getActivePanel : function()
18082     {
18083         var r = false;
18084         Roo.each(this.tabs, function(t) {
18085             if (t.active) {
18086                 r = t;
18087                 return false;
18088             }
18089             return null;
18090         });
18091         return r;
18092         
18093     },
18094     getPanelByName : function(n)
18095     {
18096         var r = false;
18097         Roo.each(this.tabs, function(t) {
18098             if (t.tabId == n) {
18099                 r = t;
18100                 return false;
18101             }
18102             return null;
18103         });
18104         return r;
18105     },
18106     indexOfPanel : function(p)
18107     {
18108         var r = false;
18109         Roo.each(this.tabs, function(t,i) {
18110             if (t.tabId == p.tabId) {
18111                 r = i;
18112                 return false;
18113             }
18114             return null;
18115         });
18116         return r;
18117     },
18118     /**
18119      * show a specific panel
18120      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18121      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18122      */
18123     showPanel : function (pan)
18124     {
18125         if(this.transition || typeof(pan) == 'undefined'){
18126             Roo.log("waiting for the transitionend");
18127             return;
18128         }
18129         
18130         if (typeof(pan) == 'number') {
18131             pan = this.tabs[pan];
18132         }
18133         
18134         if (typeof(pan) == 'string') {
18135             pan = this.getPanelByName(pan);
18136         }
18137         
18138         var cur = this.getActivePanel();
18139         
18140         if(!pan || !cur){
18141             Roo.log('pan or acitve pan is undefined');
18142             return false;
18143         }
18144         
18145         if (pan.tabId == this.getActivePanel().tabId) {
18146             return true;
18147         }
18148         
18149         if (false === cur.fireEvent('beforedeactivate')) {
18150             return false;
18151         }
18152         
18153         if(this.bullets > 0 && !Roo.isTouch){
18154             this.setActiveBullet(this.indexOfPanel(pan));
18155         }
18156         
18157         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18158             
18159             this.transition = true;
18160             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18161             var lr = dir == 'next' ? 'left' : 'right';
18162             pan.el.addClass(dir); // or prev
18163             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18164             cur.el.addClass(lr); // or right
18165             pan.el.addClass(lr);
18166             
18167             var _this = this;
18168             cur.el.on('transitionend', function() {
18169                 Roo.log("trans end?");
18170                 
18171                 pan.el.removeClass([lr,dir]);
18172                 pan.setActive(true);
18173                 
18174                 cur.el.removeClass([lr]);
18175                 cur.setActive(false);
18176                 
18177                 _this.transition = false;
18178                 
18179             }, this, { single:  true } );
18180             
18181             return true;
18182         }
18183         
18184         cur.setActive(false);
18185         pan.setActive(true);
18186         
18187         return true;
18188         
18189     },
18190     showPanelNext : function()
18191     {
18192         var i = this.indexOfPanel(this.getActivePanel());
18193         
18194         if (i >= this.tabs.length - 1 && !this.autoslide) {
18195             return;
18196         }
18197         
18198         if (i >= this.tabs.length - 1 && this.autoslide) {
18199             i = -1;
18200         }
18201         
18202         this.showPanel(this.tabs[i+1]);
18203     },
18204     
18205     showPanelPrev : function()
18206     {
18207         var i = this.indexOfPanel(this.getActivePanel());
18208         
18209         if (i  < 1 && !this.autoslide) {
18210             return;
18211         }
18212         
18213         if (i < 1 && this.autoslide) {
18214             i = this.tabs.length;
18215         }
18216         
18217         this.showPanel(this.tabs[i-1]);
18218     },
18219     
18220     
18221     addBullet: function()
18222     {
18223         if(!this.bullets || Roo.isTouch){
18224             return;
18225         }
18226         var ctr = this.el.select('.carousel-bullets',true).first();
18227         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18228         var bullet = ctr.createChild({
18229             cls : 'bullet bullet-' + i
18230         },ctr.dom.lastChild);
18231         
18232         
18233         var _this = this;
18234         
18235         bullet.on('click', (function(e, el, o, ii, t){
18236
18237             e.preventDefault();
18238
18239             this.showPanel(ii);
18240
18241             if(this.autoslide && this.slideFn){
18242                 clearInterval(this.slideFn);
18243                 this.slideFn = window.setInterval(function() {
18244                     _this.showPanelNext();
18245                 }, this.timer);
18246             }
18247
18248         }).createDelegate(this, [i, bullet], true));
18249                 
18250         
18251     },
18252      
18253     setActiveBullet : function(i)
18254     {
18255         if(Roo.isTouch){
18256             return;
18257         }
18258         
18259         Roo.each(this.el.select('.bullet', true).elements, function(el){
18260             el.removeClass('selected');
18261         });
18262
18263         var bullet = this.el.select('.bullet-' + i, true).first();
18264         
18265         if(!bullet){
18266             return;
18267         }
18268         
18269         bullet.addClass('selected');
18270     }
18271     
18272     
18273   
18274 });
18275
18276  
18277
18278  
18279  
18280 Roo.apply(Roo.bootstrap.TabGroup, {
18281     
18282     groups: {},
18283      /**
18284     * register a Navigation Group
18285     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18286     */
18287     register : function(navgrp)
18288     {
18289         this.groups[navgrp.navId] = navgrp;
18290         
18291     },
18292     /**
18293     * fetch a Navigation Group based on the navigation ID
18294     * if one does not exist , it will get created.
18295     * @param {string} the navgroup to add
18296     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18297     */
18298     get: function(navId) {
18299         if (typeof(this.groups[navId]) == 'undefined') {
18300             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18301         }
18302         return this.groups[navId] ;
18303     }
18304     
18305     
18306     
18307 });
18308
18309  /*
18310  * - LGPL
18311  *
18312  * TabPanel
18313  * 
18314  */
18315
18316 /**
18317  * @class Roo.bootstrap.TabPanel
18318  * @extends Roo.bootstrap.Component
18319  * Bootstrap TabPanel class
18320  * @cfg {Boolean} active panel active
18321  * @cfg {String} html panel content
18322  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18323  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18324  * @cfg {String} href click to link..
18325  * 
18326  * 
18327  * @constructor
18328  * Create a new TabPanel
18329  * @param {Object} config The config object
18330  */
18331
18332 Roo.bootstrap.TabPanel = function(config){
18333     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18334     this.addEvents({
18335         /**
18336              * @event changed
18337              * Fires when the active status changes
18338              * @param {Roo.bootstrap.TabPanel} this
18339              * @param {Boolean} state the new state
18340             
18341          */
18342         'changed': true,
18343         /**
18344              * @event beforedeactivate
18345              * Fires before a tab is de-activated - can be used to do validation on a form.
18346              * @param {Roo.bootstrap.TabPanel} this
18347              * @return {Boolean} false if there is an error
18348             
18349          */
18350         'beforedeactivate': true
18351      });
18352     
18353     this.tabId = this.tabId || Roo.id();
18354   
18355 };
18356
18357 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18358     
18359     active: false,
18360     html: false,
18361     tabId: false,
18362     navId : false,
18363     href : '',
18364     
18365     getAutoCreate : function(){
18366         var cfg = {
18367             tag: 'div',
18368             // item is needed for carousel - not sure if it has any effect otherwise
18369             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18370             html: this.html || ''
18371         };
18372         
18373         if(this.active){
18374             cfg.cls += ' active';
18375         }
18376         
18377         if(this.tabId){
18378             cfg.tabId = this.tabId;
18379         }
18380         
18381         
18382         return cfg;
18383     },
18384     
18385     initEvents:  function()
18386     {
18387         var p = this.parent();
18388         
18389         this.navId = this.navId || p.navId;
18390         
18391         if (typeof(this.navId) != 'undefined') {
18392             // not really needed.. but just in case.. parent should be a NavGroup.
18393             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18394             
18395             tg.register(this);
18396             
18397             var i = tg.tabs.length - 1;
18398             
18399             if(this.active && tg.bullets > 0 && i < tg.bullets){
18400                 tg.setActiveBullet(i);
18401             }
18402         }
18403         
18404         this.el.on('click', this.onClick, this);
18405         
18406         if(Roo.isTouch){
18407             this.el.on("touchstart", this.onTouchStart, this);
18408             this.el.on("touchmove", this.onTouchMove, this);
18409             this.el.on("touchend", this.onTouchEnd, this);
18410         }
18411         
18412     },
18413     
18414     onRender : function(ct, position)
18415     {
18416         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18417     },
18418     
18419     setActive : function(state)
18420     {
18421         Roo.log("panel - set active " + this.tabId + "=" + state);
18422         
18423         this.active = state;
18424         if (!state) {
18425             this.el.removeClass('active');
18426             
18427         } else  if (!this.el.hasClass('active')) {
18428             this.el.addClass('active');
18429         }
18430         
18431         this.fireEvent('changed', this, state);
18432     },
18433     
18434     onClick : function(e)
18435     {
18436         e.preventDefault();
18437         
18438         if(!this.href.length){
18439             return;
18440         }
18441         
18442         window.location.href = this.href;
18443     },
18444     
18445     startX : 0,
18446     startY : 0,
18447     endX : 0,
18448     endY : 0,
18449     swiping : false,
18450     
18451     onTouchStart : function(e)
18452     {
18453         this.swiping = false;
18454         
18455         this.startX = e.browserEvent.touches[0].clientX;
18456         this.startY = e.browserEvent.touches[0].clientY;
18457     },
18458     
18459     onTouchMove : function(e)
18460     {
18461         this.swiping = true;
18462         
18463         this.endX = e.browserEvent.touches[0].clientX;
18464         this.endY = e.browserEvent.touches[0].clientY;
18465     },
18466     
18467     onTouchEnd : function(e)
18468     {
18469         if(!this.swiping){
18470             this.onClick(e);
18471             return;
18472         }
18473         
18474         var tabGroup = this.parent();
18475         
18476         if(this.endX > this.startX){ // swiping right
18477             tabGroup.showPanelPrev();
18478             return;
18479         }
18480         
18481         if(this.startX > this.endX){ // swiping left
18482             tabGroup.showPanelNext();
18483             return;
18484         }
18485     }
18486     
18487     
18488 });
18489  
18490
18491  
18492
18493  /*
18494  * - LGPL
18495  *
18496  * DateField
18497  * 
18498  */
18499
18500 /**
18501  * @class Roo.bootstrap.DateField
18502  * @extends Roo.bootstrap.Input
18503  * Bootstrap DateField class
18504  * @cfg {Number} weekStart default 0
18505  * @cfg {String} viewMode default empty, (months|years)
18506  * @cfg {String} minViewMode default empty, (months|years)
18507  * @cfg {Number} startDate default -Infinity
18508  * @cfg {Number} endDate default Infinity
18509  * @cfg {Boolean} todayHighlight default false
18510  * @cfg {Boolean} todayBtn default false
18511  * @cfg {Boolean} calendarWeeks default false
18512  * @cfg {Object} daysOfWeekDisabled default empty
18513  * @cfg {Boolean} singleMode default false (true | false)
18514  * 
18515  * @cfg {Boolean} keyboardNavigation default true
18516  * @cfg {String} language default en
18517  * 
18518  * @constructor
18519  * Create a new DateField
18520  * @param {Object} config The config object
18521  */
18522
18523 Roo.bootstrap.DateField = function(config){
18524     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18525      this.addEvents({
18526             /**
18527              * @event show
18528              * Fires when this field show.
18529              * @param {Roo.bootstrap.DateField} this
18530              * @param {Mixed} date The date value
18531              */
18532             show : true,
18533             /**
18534              * @event show
18535              * Fires when this field hide.
18536              * @param {Roo.bootstrap.DateField} this
18537              * @param {Mixed} date The date value
18538              */
18539             hide : true,
18540             /**
18541              * @event select
18542              * Fires when select a date.
18543              * @param {Roo.bootstrap.DateField} this
18544              * @param {Mixed} date The date value
18545              */
18546             select : true,
18547             /**
18548              * @event beforeselect
18549              * Fires when before select a date.
18550              * @param {Roo.bootstrap.DateField} this
18551              * @param {Mixed} date The date value
18552              */
18553             beforeselect : true
18554         });
18555 };
18556
18557 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18558     
18559     /**
18560      * @cfg {String} format
18561      * The default date format string which can be overriden for localization support.  The format must be
18562      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18563      */
18564     format : "m/d/y",
18565     /**
18566      * @cfg {String} altFormats
18567      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18568      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18569      */
18570     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18571     
18572     weekStart : 0,
18573     
18574     viewMode : '',
18575     
18576     minViewMode : '',
18577     
18578     todayHighlight : false,
18579     
18580     todayBtn: false,
18581     
18582     language: 'en',
18583     
18584     keyboardNavigation: true,
18585     
18586     calendarWeeks: false,
18587     
18588     startDate: -Infinity,
18589     
18590     endDate: Infinity,
18591     
18592     daysOfWeekDisabled: [],
18593     
18594     _events: [],
18595     
18596     singleMode : false,
18597     
18598     UTCDate: function()
18599     {
18600         return new Date(Date.UTC.apply(Date, arguments));
18601     },
18602     
18603     UTCToday: function()
18604     {
18605         var today = new Date();
18606         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18607     },
18608     
18609     getDate: function() {
18610             var d = this.getUTCDate();
18611             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18612     },
18613     
18614     getUTCDate: function() {
18615             return this.date;
18616     },
18617     
18618     setDate: function(d) {
18619             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18620     },
18621     
18622     setUTCDate: function(d) {
18623             this.date = d;
18624             this.setValue(this.formatDate(this.date));
18625     },
18626         
18627     onRender: function(ct, position)
18628     {
18629         
18630         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18631         
18632         this.language = this.language || 'en';
18633         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18634         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18635         
18636         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18637         this.format = this.format || 'm/d/y';
18638         this.isInline = false;
18639         this.isInput = true;
18640         this.component = this.el.select('.add-on', true).first() || false;
18641         this.component = (this.component && this.component.length === 0) ? false : this.component;
18642         this.hasInput = this.component && this.inputEl().length;
18643         
18644         if (typeof(this.minViewMode === 'string')) {
18645             switch (this.minViewMode) {
18646                 case 'months':
18647                     this.minViewMode = 1;
18648                     break;
18649                 case 'years':
18650                     this.minViewMode = 2;
18651                     break;
18652                 default:
18653                     this.minViewMode = 0;
18654                     break;
18655             }
18656         }
18657         
18658         if (typeof(this.viewMode === 'string')) {
18659             switch (this.viewMode) {
18660                 case 'months':
18661                     this.viewMode = 1;
18662                     break;
18663                 case 'years':
18664                     this.viewMode = 2;
18665                     break;
18666                 default:
18667                     this.viewMode = 0;
18668                     break;
18669             }
18670         }
18671                 
18672         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18673         
18674 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18675         
18676         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18677         
18678         this.picker().on('mousedown', this.onMousedown, this);
18679         this.picker().on('click', this.onClick, this);
18680         
18681         this.picker().addClass('datepicker-dropdown');
18682         
18683         this.startViewMode = this.viewMode;
18684         
18685         if(this.singleMode){
18686             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18687                 v.setVisibilityMode(Roo.Element.DISPLAY);
18688                 v.hide();
18689             });
18690             
18691             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18692                 v.setStyle('width', '189px');
18693             });
18694         }
18695         
18696         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18697             if(!this.calendarWeeks){
18698                 v.remove();
18699                 return;
18700             }
18701             
18702             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18703             v.attr('colspan', function(i, val){
18704                 return parseInt(val) + 1;
18705             });
18706         });
18707                         
18708         
18709         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18710         
18711         this.setStartDate(this.startDate);
18712         this.setEndDate(this.endDate);
18713         
18714         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18715         
18716         this.fillDow();
18717         this.fillMonths();
18718         this.update();
18719         this.showMode();
18720         
18721         if(this.isInline) {
18722             this.showPopup();
18723         }
18724     },
18725     
18726     picker : function()
18727     {
18728         return this.pickerEl;
18729 //        return this.el.select('.datepicker', true).first();
18730     },
18731     
18732     fillDow: function()
18733     {
18734         var dowCnt = this.weekStart;
18735         
18736         var dow = {
18737             tag: 'tr',
18738             cn: [
18739                 
18740             ]
18741         };
18742         
18743         if(this.calendarWeeks){
18744             dow.cn.push({
18745                 tag: 'th',
18746                 cls: 'cw',
18747                 html: '&nbsp;'
18748             })
18749         }
18750         
18751         while (dowCnt < this.weekStart + 7) {
18752             dow.cn.push({
18753                 tag: 'th',
18754                 cls: 'dow',
18755                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18756             });
18757         }
18758         
18759         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18760     },
18761     
18762     fillMonths: function()
18763     {    
18764         var i = 0;
18765         var months = this.picker().select('>.datepicker-months td', true).first();
18766         
18767         months.dom.innerHTML = '';
18768         
18769         while (i < 12) {
18770             var month = {
18771                 tag: 'span',
18772                 cls: 'month',
18773                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18774             };
18775             
18776             months.createChild(month);
18777         }
18778         
18779     },
18780     
18781     update: function()
18782     {
18783         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;
18784         
18785         if (this.date < this.startDate) {
18786             this.viewDate = new Date(this.startDate);
18787         } else if (this.date > this.endDate) {
18788             this.viewDate = new Date(this.endDate);
18789         } else {
18790             this.viewDate = new Date(this.date);
18791         }
18792         
18793         this.fill();
18794     },
18795     
18796     fill: function() 
18797     {
18798         var d = new Date(this.viewDate),
18799                 year = d.getUTCFullYear(),
18800                 month = d.getUTCMonth(),
18801                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18802                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18803                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18804                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18805                 currentDate = this.date && this.date.valueOf(),
18806                 today = this.UTCToday();
18807         
18808         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18809         
18810 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18811         
18812 //        this.picker.select('>tfoot th.today').
18813 //                                              .text(dates[this.language].today)
18814 //                                              .toggle(this.todayBtn !== false);
18815     
18816         this.updateNavArrows();
18817         this.fillMonths();
18818                                                 
18819         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18820         
18821         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18822          
18823         prevMonth.setUTCDate(day);
18824         
18825         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18826         
18827         var nextMonth = new Date(prevMonth);
18828         
18829         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18830         
18831         nextMonth = nextMonth.valueOf();
18832         
18833         var fillMonths = false;
18834         
18835         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18836         
18837         while(prevMonth.valueOf() <= nextMonth) {
18838             var clsName = '';
18839             
18840             if (prevMonth.getUTCDay() === this.weekStart) {
18841                 if(fillMonths){
18842                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18843                 }
18844                     
18845                 fillMonths = {
18846                     tag: 'tr',
18847                     cn: []
18848                 };
18849                 
18850                 if(this.calendarWeeks){
18851                     // ISO 8601: First week contains first thursday.
18852                     // ISO also states week starts on Monday, but we can be more abstract here.
18853                     var
18854                     // Start of current week: based on weekstart/current date
18855                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18856                     // Thursday of this week
18857                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18858                     // First Thursday of year, year from thursday
18859                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18860                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18861                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18862                     
18863                     fillMonths.cn.push({
18864                         tag: 'td',
18865                         cls: 'cw',
18866                         html: calWeek
18867                     });
18868                 }
18869             }
18870             
18871             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18872                 clsName += ' old';
18873             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18874                 clsName += ' new';
18875             }
18876             if (this.todayHighlight &&
18877                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18878                 prevMonth.getUTCMonth() == today.getMonth() &&
18879                 prevMonth.getUTCDate() == today.getDate()) {
18880                 clsName += ' today';
18881             }
18882             
18883             if (currentDate && prevMonth.valueOf() === currentDate) {
18884                 clsName += ' active';
18885             }
18886             
18887             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18888                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18889                     clsName += ' disabled';
18890             }
18891             
18892             fillMonths.cn.push({
18893                 tag: 'td',
18894                 cls: 'day ' + clsName,
18895                 html: prevMonth.getDate()
18896             });
18897             
18898             prevMonth.setDate(prevMonth.getDate()+1);
18899         }
18900           
18901         var currentYear = this.date && this.date.getUTCFullYear();
18902         var currentMonth = this.date && this.date.getUTCMonth();
18903         
18904         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18905         
18906         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18907             v.removeClass('active');
18908             
18909             if(currentYear === year && k === currentMonth){
18910                 v.addClass('active');
18911             }
18912             
18913             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18914                 v.addClass('disabled');
18915             }
18916             
18917         });
18918         
18919         
18920         year = parseInt(year/10, 10) * 10;
18921         
18922         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18923         
18924         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18925         
18926         year -= 1;
18927         for (var i = -1; i < 11; i++) {
18928             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18929                 tag: 'span',
18930                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18931                 html: year
18932             });
18933             
18934             year += 1;
18935         }
18936     },
18937     
18938     showMode: function(dir) 
18939     {
18940         if (dir) {
18941             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18942         }
18943         
18944         Roo.each(this.picker().select('>div',true).elements, function(v){
18945             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18946             v.hide();
18947         });
18948         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18949     },
18950     
18951     place: function()
18952     {
18953         if(this.isInline) {
18954             return;
18955         }
18956         
18957         this.picker().removeClass(['bottom', 'top']);
18958         
18959         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18960             /*
18961              * place to the top of element!
18962              *
18963              */
18964             
18965             this.picker().addClass('top');
18966             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18967             
18968             return;
18969         }
18970         
18971         this.picker().addClass('bottom');
18972         
18973         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18974     },
18975     
18976     parseDate : function(value)
18977     {
18978         if(!value || value instanceof Date){
18979             return value;
18980         }
18981         var v = Date.parseDate(value, this.format);
18982         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18983             v = Date.parseDate(value, 'Y-m-d');
18984         }
18985         if(!v && this.altFormats){
18986             if(!this.altFormatsArray){
18987                 this.altFormatsArray = this.altFormats.split("|");
18988             }
18989             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18990                 v = Date.parseDate(value, this.altFormatsArray[i]);
18991             }
18992         }
18993         return v;
18994     },
18995     
18996     formatDate : function(date, fmt)
18997     {   
18998         return (!date || !(date instanceof Date)) ?
18999         date : date.dateFormat(fmt || this.format);
19000     },
19001     
19002     onFocus : function()
19003     {
19004         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19005         this.showPopup();
19006     },
19007     
19008     onBlur : function()
19009     {
19010         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19011         
19012         var d = this.inputEl().getValue();
19013         
19014         this.setValue(d);
19015                 
19016         this.hidePopup();
19017     },
19018     
19019     showPopup : function()
19020     {
19021         this.picker().show();
19022         this.update();
19023         this.place();
19024         
19025         this.fireEvent('showpopup', this, this.date);
19026     },
19027     
19028     hidePopup : function()
19029     {
19030         if(this.isInline) {
19031             return;
19032         }
19033         this.picker().hide();
19034         this.viewMode = this.startViewMode;
19035         this.showMode();
19036         
19037         this.fireEvent('hidepopup', this, this.date);
19038         
19039     },
19040     
19041     onMousedown: function(e)
19042     {
19043         e.stopPropagation();
19044         e.preventDefault();
19045     },
19046     
19047     keyup: function(e)
19048     {
19049         Roo.bootstrap.DateField.superclass.keyup.call(this);
19050         this.update();
19051     },
19052
19053     setValue: function(v)
19054     {
19055         if(this.fireEvent('beforeselect', this, v) !== false){
19056             var d = new Date(this.parseDate(v) ).clearTime();
19057         
19058             if(isNaN(d.getTime())){
19059                 this.date = this.viewDate = '';
19060                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19061                 return;
19062             }
19063
19064             v = this.formatDate(d);
19065
19066             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19067
19068             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19069
19070             this.update();
19071
19072             this.fireEvent('select', this, this.date);
19073         }
19074     },
19075     
19076     getValue: function()
19077     {
19078         return this.formatDate(this.date);
19079     },
19080     
19081     fireKey: function(e)
19082     {
19083         if (!this.picker().isVisible()){
19084             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19085                 this.showPopup();
19086             }
19087             return;
19088         }
19089         
19090         var dateChanged = false,
19091         dir, day, month,
19092         newDate, newViewDate;
19093         
19094         switch(e.keyCode){
19095             case 27: // escape
19096                 this.hidePopup();
19097                 e.preventDefault();
19098                 break;
19099             case 37: // left
19100             case 39: // right
19101                 if (!this.keyboardNavigation) {
19102                     break;
19103                 }
19104                 dir = e.keyCode == 37 ? -1 : 1;
19105                 
19106                 if (e.ctrlKey){
19107                     newDate = this.moveYear(this.date, dir);
19108                     newViewDate = this.moveYear(this.viewDate, dir);
19109                 } else if (e.shiftKey){
19110                     newDate = this.moveMonth(this.date, dir);
19111                     newViewDate = this.moveMonth(this.viewDate, dir);
19112                 } else {
19113                     newDate = new Date(this.date);
19114                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19115                     newViewDate = new Date(this.viewDate);
19116                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19117                 }
19118                 if (this.dateWithinRange(newDate)){
19119                     this.date = newDate;
19120                     this.viewDate = newViewDate;
19121                     this.setValue(this.formatDate(this.date));
19122 //                    this.update();
19123                     e.preventDefault();
19124                     dateChanged = true;
19125                 }
19126                 break;
19127             case 38: // up
19128             case 40: // down
19129                 if (!this.keyboardNavigation) {
19130                     break;
19131                 }
19132                 dir = e.keyCode == 38 ? -1 : 1;
19133                 if (e.ctrlKey){
19134                     newDate = this.moveYear(this.date, dir);
19135                     newViewDate = this.moveYear(this.viewDate, dir);
19136                 } else if (e.shiftKey){
19137                     newDate = this.moveMonth(this.date, dir);
19138                     newViewDate = this.moveMonth(this.viewDate, dir);
19139                 } else {
19140                     newDate = new Date(this.date);
19141                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19142                     newViewDate = new Date(this.viewDate);
19143                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19144                 }
19145                 if (this.dateWithinRange(newDate)){
19146                     this.date = newDate;
19147                     this.viewDate = newViewDate;
19148                     this.setValue(this.formatDate(this.date));
19149 //                    this.update();
19150                     e.preventDefault();
19151                     dateChanged = true;
19152                 }
19153                 break;
19154             case 13: // enter
19155                 this.setValue(this.formatDate(this.date));
19156                 this.hidePopup();
19157                 e.preventDefault();
19158                 break;
19159             case 9: // tab
19160                 this.setValue(this.formatDate(this.date));
19161                 this.hidePopup();
19162                 break;
19163             case 16: // shift
19164             case 17: // ctrl
19165             case 18: // alt
19166                 break;
19167             default :
19168                 this.hide();
19169                 
19170         }
19171     },
19172     
19173     
19174     onClick: function(e) 
19175     {
19176         e.stopPropagation();
19177         e.preventDefault();
19178         
19179         var target = e.getTarget();
19180         
19181         if(target.nodeName.toLowerCase() === 'i'){
19182             target = Roo.get(target).dom.parentNode;
19183         }
19184         
19185         var nodeName = target.nodeName;
19186         var className = target.className;
19187         var html = target.innerHTML;
19188         //Roo.log(nodeName);
19189         
19190         switch(nodeName.toLowerCase()) {
19191             case 'th':
19192                 switch(className) {
19193                     case 'switch':
19194                         this.showMode(1);
19195                         break;
19196                     case 'prev':
19197                     case 'next':
19198                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19199                         switch(this.viewMode){
19200                                 case 0:
19201                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19202                                         break;
19203                                 case 1:
19204                                 case 2:
19205                                         this.viewDate = this.moveYear(this.viewDate, dir);
19206                                         break;
19207                         }
19208                         this.fill();
19209                         break;
19210                     case 'today':
19211                         var date = new Date();
19212                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19213 //                        this.fill()
19214                         this.setValue(this.formatDate(this.date));
19215                         
19216                         this.hidePopup();
19217                         break;
19218                 }
19219                 break;
19220             case 'span':
19221                 if (className.indexOf('disabled') < 0) {
19222                     this.viewDate.setUTCDate(1);
19223                     if (className.indexOf('month') > -1) {
19224                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19225                     } else {
19226                         var year = parseInt(html, 10) || 0;
19227                         this.viewDate.setUTCFullYear(year);
19228                         
19229                     }
19230                     
19231                     if(this.singleMode){
19232                         this.setValue(this.formatDate(this.viewDate));
19233                         this.hidePopup();
19234                         return;
19235                     }
19236                     
19237                     this.showMode(-1);
19238                     this.fill();
19239                 }
19240                 break;
19241                 
19242             case 'td':
19243                 //Roo.log(className);
19244                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19245                     var day = parseInt(html, 10) || 1;
19246                     var year = this.viewDate.getUTCFullYear(),
19247                         month = this.viewDate.getUTCMonth();
19248
19249                     if (className.indexOf('old') > -1) {
19250                         if(month === 0 ){
19251                             month = 11;
19252                             year -= 1;
19253                         }else{
19254                             month -= 1;
19255                         }
19256                     } else if (className.indexOf('new') > -1) {
19257                         if (month == 11) {
19258                             month = 0;
19259                             year += 1;
19260                         } else {
19261                             month += 1;
19262                         }
19263                     }
19264                     //Roo.log([year,month,day]);
19265                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19266                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19267 //                    this.fill();
19268                     //Roo.log(this.formatDate(this.date));
19269                     this.setValue(this.formatDate(this.date));
19270                     this.hidePopup();
19271                 }
19272                 break;
19273         }
19274     },
19275     
19276     setStartDate: function(startDate)
19277     {
19278         this.startDate = startDate || -Infinity;
19279         if (this.startDate !== -Infinity) {
19280             this.startDate = this.parseDate(this.startDate);
19281         }
19282         this.update();
19283         this.updateNavArrows();
19284     },
19285
19286     setEndDate: function(endDate)
19287     {
19288         this.endDate = endDate || Infinity;
19289         if (this.endDate !== Infinity) {
19290             this.endDate = this.parseDate(this.endDate);
19291         }
19292         this.update();
19293         this.updateNavArrows();
19294     },
19295     
19296     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19297     {
19298         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19299         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19300             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19301         }
19302         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19303             return parseInt(d, 10);
19304         });
19305         this.update();
19306         this.updateNavArrows();
19307     },
19308     
19309     updateNavArrows: function() 
19310     {
19311         if(this.singleMode){
19312             return;
19313         }
19314         
19315         var d = new Date(this.viewDate),
19316         year = d.getUTCFullYear(),
19317         month = d.getUTCMonth();
19318         
19319         Roo.each(this.picker().select('.prev', true).elements, function(v){
19320             v.show();
19321             switch (this.viewMode) {
19322                 case 0:
19323
19324                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19325                         v.hide();
19326                     }
19327                     break;
19328                 case 1:
19329                 case 2:
19330                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19331                         v.hide();
19332                     }
19333                     break;
19334             }
19335         });
19336         
19337         Roo.each(this.picker().select('.next', true).elements, function(v){
19338             v.show();
19339             switch (this.viewMode) {
19340                 case 0:
19341
19342                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19343                         v.hide();
19344                     }
19345                     break;
19346                 case 1:
19347                 case 2:
19348                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19349                         v.hide();
19350                     }
19351                     break;
19352             }
19353         })
19354     },
19355     
19356     moveMonth: function(date, dir)
19357     {
19358         if (!dir) {
19359             return date;
19360         }
19361         var new_date = new Date(date.valueOf()),
19362         day = new_date.getUTCDate(),
19363         month = new_date.getUTCMonth(),
19364         mag = Math.abs(dir),
19365         new_month, test;
19366         dir = dir > 0 ? 1 : -1;
19367         if (mag == 1){
19368             test = dir == -1
19369             // If going back one month, make sure month is not current month
19370             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19371             ? function(){
19372                 return new_date.getUTCMonth() == month;
19373             }
19374             // If going forward one month, make sure month is as expected
19375             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19376             : function(){
19377                 return new_date.getUTCMonth() != new_month;
19378             };
19379             new_month = month + dir;
19380             new_date.setUTCMonth(new_month);
19381             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19382             if (new_month < 0 || new_month > 11) {
19383                 new_month = (new_month + 12) % 12;
19384             }
19385         } else {
19386             // For magnitudes >1, move one month at a time...
19387             for (var i=0; i<mag; i++) {
19388                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19389                 new_date = this.moveMonth(new_date, dir);
19390             }
19391             // ...then reset the day, keeping it in the new month
19392             new_month = new_date.getUTCMonth();
19393             new_date.setUTCDate(day);
19394             test = function(){
19395                 return new_month != new_date.getUTCMonth();
19396             };
19397         }
19398         // Common date-resetting loop -- if date is beyond end of month, make it
19399         // end of month
19400         while (test()){
19401             new_date.setUTCDate(--day);
19402             new_date.setUTCMonth(new_month);
19403         }
19404         return new_date;
19405     },
19406
19407     moveYear: function(date, dir)
19408     {
19409         return this.moveMonth(date, dir*12);
19410     },
19411
19412     dateWithinRange: function(date)
19413     {
19414         return date >= this.startDate && date <= this.endDate;
19415     },
19416
19417     
19418     remove: function() 
19419     {
19420         this.picker().remove();
19421     },
19422     
19423     validateValue : function(value)
19424     {
19425         if(this.getVisibilityEl().hasClass('hidden')){
19426             return true;
19427         }
19428         
19429         if(value.length < 1)  {
19430             if(this.allowBlank){
19431                 return true;
19432             }
19433             return false;
19434         }
19435         
19436         if(value.length < this.minLength){
19437             return false;
19438         }
19439         if(value.length > this.maxLength){
19440             return false;
19441         }
19442         if(this.vtype){
19443             var vt = Roo.form.VTypes;
19444             if(!vt[this.vtype](value, this)){
19445                 return false;
19446             }
19447         }
19448         if(typeof this.validator == "function"){
19449             var msg = this.validator(value);
19450             if(msg !== true){
19451                 return false;
19452             }
19453         }
19454         
19455         if(this.regex && !this.regex.test(value)){
19456             return false;
19457         }
19458         
19459         if(typeof(this.parseDate(value)) == 'undefined'){
19460             return false;
19461         }
19462         
19463         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19464             return false;
19465         }      
19466         
19467         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19468             return false;
19469         } 
19470         
19471         
19472         return true;
19473     },
19474     
19475     reset : function()
19476     {
19477         this.date = this.viewDate = '';
19478         
19479         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19480     }
19481    
19482 });
19483
19484 Roo.apply(Roo.bootstrap.DateField,  {
19485     
19486     head : {
19487         tag: 'thead',
19488         cn: [
19489         {
19490             tag: 'tr',
19491             cn: [
19492             {
19493                 tag: 'th',
19494                 cls: 'prev',
19495                 html: '<i class="fa fa-arrow-left"/>'
19496             },
19497             {
19498                 tag: 'th',
19499                 cls: 'switch',
19500                 colspan: '5'
19501             },
19502             {
19503                 tag: 'th',
19504                 cls: 'next',
19505                 html: '<i class="fa fa-arrow-right"/>'
19506             }
19507
19508             ]
19509         }
19510         ]
19511     },
19512     
19513     content : {
19514         tag: 'tbody',
19515         cn: [
19516         {
19517             tag: 'tr',
19518             cn: [
19519             {
19520                 tag: 'td',
19521                 colspan: '7'
19522             }
19523             ]
19524         }
19525         ]
19526     },
19527     
19528     footer : {
19529         tag: 'tfoot',
19530         cn: [
19531         {
19532             tag: 'tr',
19533             cn: [
19534             {
19535                 tag: 'th',
19536                 colspan: '7',
19537                 cls: 'today'
19538             }
19539                     
19540             ]
19541         }
19542         ]
19543     },
19544     
19545     dates:{
19546         en: {
19547             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19548             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19549             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19550             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19551             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19552             today: "Today"
19553         }
19554     },
19555     
19556     modes: [
19557     {
19558         clsName: 'days',
19559         navFnc: 'Month',
19560         navStep: 1
19561     },
19562     {
19563         clsName: 'months',
19564         navFnc: 'FullYear',
19565         navStep: 1
19566     },
19567     {
19568         clsName: 'years',
19569         navFnc: 'FullYear',
19570         navStep: 10
19571     }]
19572 });
19573
19574 Roo.apply(Roo.bootstrap.DateField,  {
19575   
19576     template : {
19577         tag: 'div',
19578         cls: 'datepicker dropdown-menu roo-dynamic',
19579         cn: [
19580         {
19581             tag: 'div',
19582             cls: 'datepicker-days',
19583             cn: [
19584             {
19585                 tag: 'table',
19586                 cls: 'table-condensed',
19587                 cn:[
19588                 Roo.bootstrap.DateField.head,
19589                 {
19590                     tag: 'tbody'
19591                 },
19592                 Roo.bootstrap.DateField.footer
19593                 ]
19594             }
19595             ]
19596         },
19597         {
19598             tag: 'div',
19599             cls: 'datepicker-months',
19600             cn: [
19601             {
19602                 tag: 'table',
19603                 cls: 'table-condensed',
19604                 cn:[
19605                 Roo.bootstrap.DateField.head,
19606                 Roo.bootstrap.DateField.content,
19607                 Roo.bootstrap.DateField.footer
19608                 ]
19609             }
19610             ]
19611         },
19612         {
19613             tag: 'div',
19614             cls: 'datepicker-years',
19615             cn: [
19616             {
19617                 tag: 'table',
19618                 cls: 'table-condensed',
19619                 cn:[
19620                 Roo.bootstrap.DateField.head,
19621                 Roo.bootstrap.DateField.content,
19622                 Roo.bootstrap.DateField.footer
19623                 ]
19624             }
19625             ]
19626         }
19627         ]
19628     }
19629 });
19630
19631  
19632
19633  /*
19634  * - LGPL
19635  *
19636  * TimeField
19637  * 
19638  */
19639
19640 /**
19641  * @class Roo.bootstrap.TimeField
19642  * @extends Roo.bootstrap.Input
19643  * Bootstrap DateField class
19644  * 
19645  * 
19646  * @constructor
19647  * Create a new TimeField
19648  * @param {Object} config The config object
19649  */
19650
19651 Roo.bootstrap.TimeField = function(config){
19652     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19653     this.addEvents({
19654             /**
19655              * @event show
19656              * Fires when this field show.
19657              * @param {Roo.bootstrap.DateField} thisthis
19658              * @param {Mixed} date The date value
19659              */
19660             show : true,
19661             /**
19662              * @event show
19663              * Fires when this field hide.
19664              * @param {Roo.bootstrap.DateField} this
19665              * @param {Mixed} date The date value
19666              */
19667             hide : true,
19668             /**
19669              * @event select
19670              * Fires when select a date.
19671              * @param {Roo.bootstrap.DateField} this
19672              * @param {Mixed} date The date value
19673              */
19674             select : true
19675         });
19676 };
19677
19678 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19679     
19680     /**
19681      * @cfg {String} format
19682      * The default time format string which can be overriden for localization support.  The format must be
19683      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19684      */
19685     format : "H:i",
19686        
19687     onRender: function(ct, position)
19688     {
19689         
19690         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19691                 
19692         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19693         
19694         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19695         
19696         this.pop = this.picker().select('>.datepicker-time',true).first();
19697         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19698         
19699         this.picker().on('mousedown', this.onMousedown, this);
19700         this.picker().on('click', this.onClick, this);
19701         
19702         this.picker().addClass('datepicker-dropdown');
19703     
19704         this.fillTime();
19705         this.update();
19706             
19707         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19708         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19709         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19710         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19711         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19712         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19713
19714     },
19715     
19716     fireKey: function(e){
19717         if (!this.picker().isVisible()){
19718             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19719                 this.show();
19720             }
19721             return;
19722         }
19723
19724         e.preventDefault();
19725         
19726         switch(e.keyCode){
19727             case 27: // escape
19728                 this.hide();
19729                 break;
19730             case 37: // left
19731             case 39: // right
19732                 this.onTogglePeriod();
19733                 break;
19734             case 38: // up
19735                 this.onIncrementMinutes();
19736                 break;
19737             case 40: // down
19738                 this.onDecrementMinutes();
19739                 break;
19740             case 13: // enter
19741             case 9: // tab
19742                 this.setTime();
19743                 break;
19744         }
19745     },
19746     
19747     onClick: function(e) {
19748         e.stopPropagation();
19749         e.preventDefault();
19750     },
19751     
19752     picker : function()
19753     {
19754         return this.el.select('.datepicker', true).first();
19755     },
19756     
19757     fillTime: function()
19758     {    
19759         var time = this.pop.select('tbody', true).first();
19760         
19761         time.dom.innerHTML = '';
19762         
19763         time.createChild({
19764             tag: 'tr',
19765             cn: [
19766                 {
19767                     tag: 'td',
19768                     cn: [
19769                         {
19770                             tag: 'a',
19771                             href: '#',
19772                             cls: 'btn',
19773                             cn: [
19774                                 {
19775                                     tag: 'span',
19776                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19777                                 }
19778                             ]
19779                         } 
19780                     ]
19781                 },
19782                 {
19783                     tag: 'td',
19784                     cls: 'separator'
19785                 },
19786                 {
19787                     tag: 'td',
19788                     cn: [
19789                         {
19790                             tag: 'a',
19791                             href: '#',
19792                             cls: 'btn',
19793                             cn: [
19794                                 {
19795                                     tag: 'span',
19796                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19797                                 }
19798                             ]
19799                         }
19800                     ]
19801                 },
19802                 {
19803                     tag: 'td',
19804                     cls: 'separator'
19805                 }
19806             ]
19807         });
19808         
19809         time.createChild({
19810             tag: 'tr',
19811             cn: [
19812                 {
19813                     tag: 'td',
19814                     cn: [
19815                         {
19816                             tag: 'span',
19817                             cls: 'timepicker-hour',
19818                             html: '00'
19819                         }  
19820                     ]
19821                 },
19822                 {
19823                     tag: 'td',
19824                     cls: 'separator',
19825                     html: ':'
19826                 },
19827                 {
19828                     tag: 'td',
19829                     cn: [
19830                         {
19831                             tag: 'span',
19832                             cls: 'timepicker-minute',
19833                             html: '00'
19834                         }  
19835                     ]
19836                 },
19837                 {
19838                     tag: 'td',
19839                     cls: 'separator'
19840                 },
19841                 {
19842                     tag: 'td',
19843                     cn: [
19844                         {
19845                             tag: 'button',
19846                             type: 'button',
19847                             cls: 'btn btn-primary period',
19848                             html: 'AM'
19849                             
19850                         }
19851                     ]
19852                 }
19853             ]
19854         });
19855         
19856         time.createChild({
19857             tag: 'tr',
19858             cn: [
19859                 {
19860                     tag: 'td',
19861                     cn: [
19862                         {
19863                             tag: 'a',
19864                             href: '#',
19865                             cls: 'btn',
19866                             cn: [
19867                                 {
19868                                     tag: 'span',
19869                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19870                                 }
19871                             ]
19872                         }
19873                     ]
19874                 },
19875                 {
19876                     tag: 'td',
19877                     cls: 'separator'
19878                 },
19879                 {
19880                     tag: 'td',
19881                     cn: [
19882                         {
19883                             tag: 'a',
19884                             href: '#',
19885                             cls: 'btn',
19886                             cn: [
19887                                 {
19888                                     tag: 'span',
19889                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19890                                 }
19891                             ]
19892                         }
19893                     ]
19894                 },
19895                 {
19896                     tag: 'td',
19897                     cls: 'separator'
19898                 }
19899             ]
19900         });
19901         
19902     },
19903     
19904     update: function()
19905     {
19906         
19907         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19908         
19909         this.fill();
19910     },
19911     
19912     fill: function() 
19913     {
19914         var hours = this.time.getHours();
19915         var minutes = this.time.getMinutes();
19916         var period = 'AM';
19917         
19918         if(hours > 11){
19919             period = 'PM';
19920         }
19921         
19922         if(hours == 0){
19923             hours = 12;
19924         }
19925         
19926         
19927         if(hours > 12){
19928             hours = hours - 12;
19929         }
19930         
19931         if(hours < 10){
19932             hours = '0' + hours;
19933         }
19934         
19935         if(minutes < 10){
19936             minutes = '0' + minutes;
19937         }
19938         
19939         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19940         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19941         this.pop.select('button', true).first().dom.innerHTML = period;
19942         
19943     },
19944     
19945     place: function()
19946     {   
19947         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19948         
19949         var cls = ['bottom'];
19950         
19951         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19952             cls.pop();
19953             cls.push('top');
19954         }
19955         
19956         cls.push('right');
19957         
19958         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19959             cls.pop();
19960             cls.push('left');
19961         }
19962         
19963         this.picker().addClass(cls.join('-'));
19964         
19965         var _this = this;
19966         
19967         Roo.each(cls, function(c){
19968             if(c == 'bottom'){
19969                 _this.picker().setTop(_this.inputEl().getHeight());
19970                 return;
19971             }
19972             if(c == 'top'){
19973                 _this.picker().setTop(0 - _this.picker().getHeight());
19974                 return;
19975             }
19976             
19977             if(c == 'left'){
19978                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19979                 return;
19980             }
19981             if(c == 'right'){
19982                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19983                 return;
19984             }
19985         });
19986         
19987     },
19988   
19989     onFocus : function()
19990     {
19991         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19992         this.show();
19993     },
19994     
19995     onBlur : function()
19996     {
19997         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19998         this.hide();
19999     },
20000     
20001     show : function()
20002     {
20003         this.picker().show();
20004         this.pop.show();
20005         this.update();
20006         this.place();
20007         
20008         this.fireEvent('show', this, this.date);
20009     },
20010     
20011     hide : function()
20012     {
20013         this.picker().hide();
20014         this.pop.hide();
20015         
20016         this.fireEvent('hide', this, this.date);
20017     },
20018     
20019     setTime : function()
20020     {
20021         this.hide();
20022         this.setValue(this.time.format(this.format));
20023         
20024         this.fireEvent('select', this, this.date);
20025         
20026         
20027     },
20028     
20029     onMousedown: function(e){
20030         e.stopPropagation();
20031         e.preventDefault();
20032     },
20033     
20034     onIncrementHours: function()
20035     {
20036         Roo.log('onIncrementHours');
20037         this.time = this.time.add(Date.HOUR, 1);
20038         this.update();
20039         
20040     },
20041     
20042     onDecrementHours: function()
20043     {
20044         Roo.log('onDecrementHours');
20045         this.time = this.time.add(Date.HOUR, -1);
20046         this.update();
20047     },
20048     
20049     onIncrementMinutes: function()
20050     {
20051         Roo.log('onIncrementMinutes');
20052         this.time = this.time.add(Date.MINUTE, 1);
20053         this.update();
20054     },
20055     
20056     onDecrementMinutes: function()
20057     {
20058         Roo.log('onDecrementMinutes');
20059         this.time = this.time.add(Date.MINUTE, -1);
20060         this.update();
20061     },
20062     
20063     onTogglePeriod: function()
20064     {
20065         Roo.log('onTogglePeriod');
20066         this.time = this.time.add(Date.HOUR, 12);
20067         this.update();
20068     }
20069     
20070    
20071 });
20072
20073 Roo.apply(Roo.bootstrap.TimeField,  {
20074     
20075     content : {
20076         tag: 'tbody',
20077         cn: [
20078             {
20079                 tag: 'tr',
20080                 cn: [
20081                 {
20082                     tag: 'td',
20083                     colspan: '7'
20084                 }
20085                 ]
20086             }
20087         ]
20088     },
20089     
20090     footer : {
20091         tag: 'tfoot',
20092         cn: [
20093             {
20094                 tag: 'tr',
20095                 cn: [
20096                 {
20097                     tag: 'th',
20098                     colspan: '7',
20099                     cls: '',
20100                     cn: [
20101                         {
20102                             tag: 'button',
20103                             cls: 'btn btn-info ok',
20104                             html: 'OK'
20105                         }
20106                     ]
20107                 }
20108
20109                 ]
20110             }
20111         ]
20112     }
20113 });
20114
20115 Roo.apply(Roo.bootstrap.TimeField,  {
20116   
20117     template : {
20118         tag: 'div',
20119         cls: 'datepicker dropdown-menu',
20120         cn: [
20121             {
20122                 tag: 'div',
20123                 cls: 'datepicker-time',
20124                 cn: [
20125                 {
20126                     tag: 'table',
20127                     cls: 'table-condensed',
20128                     cn:[
20129                     Roo.bootstrap.TimeField.content,
20130                     Roo.bootstrap.TimeField.footer
20131                     ]
20132                 }
20133                 ]
20134             }
20135         ]
20136     }
20137 });
20138
20139  
20140
20141  /*
20142  * - LGPL
20143  *
20144  * MonthField
20145  * 
20146  */
20147
20148 /**
20149  * @class Roo.bootstrap.MonthField
20150  * @extends Roo.bootstrap.Input
20151  * Bootstrap MonthField class
20152  * 
20153  * @cfg {String} language default en
20154  * 
20155  * @constructor
20156  * Create a new MonthField
20157  * @param {Object} config The config object
20158  */
20159
20160 Roo.bootstrap.MonthField = function(config){
20161     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20162     
20163     this.addEvents({
20164         /**
20165          * @event show
20166          * Fires when this field show.
20167          * @param {Roo.bootstrap.MonthField} this
20168          * @param {Mixed} date The date value
20169          */
20170         show : true,
20171         /**
20172          * @event show
20173          * Fires when this field hide.
20174          * @param {Roo.bootstrap.MonthField} this
20175          * @param {Mixed} date The date value
20176          */
20177         hide : true,
20178         /**
20179          * @event select
20180          * Fires when select a date.
20181          * @param {Roo.bootstrap.MonthField} this
20182          * @param {String} oldvalue The old value
20183          * @param {String} newvalue The new value
20184          */
20185         select : true
20186     });
20187 };
20188
20189 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20190     
20191     onRender: function(ct, position)
20192     {
20193         
20194         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20195         
20196         this.language = this.language || 'en';
20197         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20198         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20199         
20200         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20201         this.isInline = false;
20202         this.isInput = true;
20203         this.component = this.el.select('.add-on', true).first() || false;
20204         this.component = (this.component && this.component.length === 0) ? false : this.component;
20205         this.hasInput = this.component && this.inputEL().length;
20206         
20207         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20208         
20209         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20210         
20211         this.picker().on('mousedown', this.onMousedown, this);
20212         this.picker().on('click', this.onClick, this);
20213         
20214         this.picker().addClass('datepicker-dropdown');
20215         
20216         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20217             v.setStyle('width', '189px');
20218         });
20219         
20220         this.fillMonths();
20221         
20222         this.update();
20223         
20224         if(this.isInline) {
20225             this.show();
20226         }
20227         
20228     },
20229     
20230     setValue: function(v, suppressEvent)
20231     {   
20232         var o = this.getValue();
20233         
20234         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20235         
20236         this.update();
20237
20238         if(suppressEvent !== true){
20239             this.fireEvent('select', this, o, v);
20240         }
20241         
20242     },
20243     
20244     getValue: function()
20245     {
20246         return this.value;
20247     },
20248     
20249     onClick: function(e) 
20250     {
20251         e.stopPropagation();
20252         e.preventDefault();
20253         
20254         var target = e.getTarget();
20255         
20256         if(target.nodeName.toLowerCase() === 'i'){
20257             target = Roo.get(target).dom.parentNode;
20258         }
20259         
20260         var nodeName = target.nodeName;
20261         var className = target.className;
20262         var html = target.innerHTML;
20263         
20264         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20265             return;
20266         }
20267         
20268         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20269         
20270         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20271         
20272         this.hide();
20273                         
20274     },
20275     
20276     picker : function()
20277     {
20278         return this.pickerEl;
20279     },
20280     
20281     fillMonths: function()
20282     {    
20283         var i = 0;
20284         var months = this.picker().select('>.datepicker-months td', true).first();
20285         
20286         months.dom.innerHTML = '';
20287         
20288         while (i < 12) {
20289             var month = {
20290                 tag: 'span',
20291                 cls: 'month',
20292                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20293             };
20294             
20295             months.createChild(month);
20296         }
20297         
20298     },
20299     
20300     update: function()
20301     {
20302         var _this = this;
20303         
20304         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20305             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20306         }
20307         
20308         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20309             e.removeClass('active');
20310             
20311             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20312                 e.addClass('active');
20313             }
20314         })
20315     },
20316     
20317     place: function()
20318     {
20319         if(this.isInline) {
20320             return;
20321         }
20322         
20323         this.picker().removeClass(['bottom', 'top']);
20324         
20325         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20326             /*
20327              * place to the top of element!
20328              *
20329              */
20330             
20331             this.picker().addClass('top');
20332             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20333             
20334             return;
20335         }
20336         
20337         this.picker().addClass('bottom');
20338         
20339         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20340     },
20341     
20342     onFocus : function()
20343     {
20344         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20345         this.show();
20346     },
20347     
20348     onBlur : function()
20349     {
20350         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20351         
20352         var d = this.inputEl().getValue();
20353         
20354         this.setValue(d);
20355                 
20356         this.hide();
20357     },
20358     
20359     show : function()
20360     {
20361         this.picker().show();
20362         this.picker().select('>.datepicker-months', true).first().show();
20363         this.update();
20364         this.place();
20365         
20366         this.fireEvent('show', this, this.date);
20367     },
20368     
20369     hide : function()
20370     {
20371         if(this.isInline) {
20372             return;
20373         }
20374         this.picker().hide();
20375         this.fireEvent('hide', this, this.date);
20376         
20377     },
20378     
20379     onMousedown: function(e)
20380     {
20381         e.stopPropagation();
20382         e.preventDefault();
20383     },
20384     
20385     keyup: function(e)
20386     {
20387         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20388         this.update();
20389     },
20390
20391     fireKey: function(e)
20392     {
20393         if (!this.picker().isVisible()){
20394             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20395                 this.show();
20396             }
20397             return;
20398         }
20399         
20400         var dir;
20401         
20402         switch(e.keyCode){
20403             case 27: // escape
20404                 this.hide();
20405                 e.preventDefault();
20406                 break;
20407             case 37: // left
20408             case 39: // right
20409                 dir = e.keyCode == 37 ? -1 : 1;
20410                 
20411                 this.vIndex = this.vIndex + dir;
20412                 
20413                 if(this.vIndex < 0){
20414                     this.vIndex = 0;
20415                 }
20416                 
20417                 if(this.vIndex > 11){
20418                     this.vIndex = 11;
20419                 }
20420                 
20421                 if(isNaN(this.vIndex)){
20422                     this.vIndex = 0;
20423                 }
20424                 
20425                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20426                 
20427                 break;
20428             case 38: // up
20429             case 40: // down
20430                 
20431                 dir = e.keyCode == 38 ? -1 : 1;
20432                 
20433                 this.vIndex = this.vIndex + dir * 4;
20434                 
20435                 if(this.vIndex < 0){
20436                     this.vIndex = 0;
20437                 }
20438                 
20439                 if(this.vIndex > 11){
20440                     this.vIndex = 11;
20441                 }
20442                 
20443                 if(isNaN(this.vIndex)){
20444                     this.vIndex = 0;
20445                 }
20446                 
20447                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20448                 break;
20449                 
20450             case 13: // enter
20451                 
20452                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20453                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20454                 }
20455                 
20456                 this.hide();
20457                 e.preventDefault();
20458                 break;
20459             case 9: // tab
20460                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20461                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20462                 }
20463                 this.hide();
20464                 break;
20465             case 16: // shift
20466             case 17: // ctrl
20467             case 18: // alt
20468                 break;
20469             default :
20470                 this.hide();
20471                 
20472         }
20473     },
20474     
20475     remove: function() 
20476     {
20477         this.picker().remove();
20478     }
20479    
20480 });
20481
20482 Roo.apply(Roo.bootstrap.MonthField,  {
20483     
20484     content : {
20485         tag: 'tbody',
20486         cn: [
20487         {
20488             tag: 'tr',
20489             cn: [
20490             {
20491                 tag: 'td',
20492                 colspan: '7'
20493             }
20494             ]
20495         }
20496         ]
20497     },
20498     
20499     dates:{
20500         en: {
20501             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20502             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20503         }
20504     }
20505 });
20506
20507 Roo.apply(Roo.bootstrap.MonthField,  {
20508   
20509     template : {
20510         tag: 'div',
20511         cls: 'datepicker dropdown-menu roo-dynamic',
20512         cn: [
20513             {
20514                 tag: 'div',
20515                 cls: 'datepicker-months',
20516                 cn: [
20517                 {
20518                     tag: 'table',
20519                     cls: 'table-condensed',
20520                     cn:[
20521                         Roo.bootstrap.DateField.content
20522                     ]
20523                 }
20524                 ]
20525             }
20526         ]
20527     }
20528 });
20529
20530  
20531
20532  
20533  /*
20534  * - LGPL
20535  *
20536  * CheckBox
20537  * 
20538  */
20539
20540 /**
20541  * @class Roo.bootstrap.CheckBox
20542  * @extends Roo.bootstrap.Input
20543  * Bootstrap CheckBox class
20544  * 
20545  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20546  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20547  * @cfg {String} boxLabel The text that appears beside the checkbox
20548  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20549  * @cfg {Boolean} checked initnal the element
20550  * @cfg {Boolean} inline inline the element (default false)
20551  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20552  * @cfg {String} tooltip label tooltip
20553  * 
20554  * @constructor
20555  * Create a new CheckBox
20556  * @param {Object} config The config object
20557  */
20558
20559 Roo.bootstrap.CheckBox = function(config){
20560     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20561    
20562     this.addEvents({
20563         /**
20564         * @event check
20565         * Fires when the element is checked or unchecked.
20566         * @param {Roo.bootstrap.CheckBox} this This input
20567         * @param {Boolean} checked The new checked value
20568         */
20569        check : true,
20570        /**
20571         * @event click
20572         * Fires when the element is click.
20573         * @param {Roo.bootstrap.CheckBox} this This input
20574         */
20575        click : true
20576     });
20577     
20578 };
20579
20580 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20581   
20582     inputType: 'checkbox',
20583     inputValue: 1,
20584     valueOff: 0,
20585     boxLabel: false,
20586     checked: false,
20587     weight : false,
20588     inline: false,
20589     tooltip : '',
20590     
20591     getAutoCreate : function()
20592     {
20593         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20594         
20595         var id = Roo.id();
20596         
20597         var cfg = {};
20598         
20599         cfg.cls = 'form-group ' + this.inputType; //input-group
20600         
20601         if(this.inline){
20602             cfg.cls += ' ' + this.inputType + '-inline';
20603         }
20604         
20605         var input =  {
20606             tag: 'input',
20607             id : id,
20608             type : this.inputType,
20609             value : this.inputValue,
20610             cls : 'roo-' + this.inputType, //'form-box',
20611             placeholder : this.placeholder || ''
20612             
20613         };
20614         
20615         if(this.inputType != 'radio'){
20616             var hidden =  {
20617                 tag: 'input',
20618                 type : 'hidden',
20619                 cls : 'roo-hidden-value',
20620                 value : this.checked ? this.inputValue : this.valueOff
20621             };
20622         }
20623         
20624             
20625         if (this.weight) { // Validity check?
20626             cfg.cls += " " + this.inputType + "-" + this.weight;
20627         }
20628         
20629         if (this.disabled) {
20630             input.disabled=true;
20631         }
20632         
20633         if(this.checked){
20634             input.checked = this.checked;
20635         }
20636         
20637         if (this.name) {
20638             
20639             input.name = this.name;
20640             
20641             if(this.inputType != 'radio'){
20642                 hidden.name = this.name;
20643                 input.name = '_hidden_' + this.name;
20644             }
20645         }
20646         
20647         if (this.size) {
20648             input.cls += ' input-' + this.size;
20649         }
20650         
20651         var settings=this;
20652         
20653         ['xs','sm','md','lg'].map(function(size){
20654             if (settings[size]) {
20655                 cfg.cls += ' col-' + size + '-' + settings[size];
20656             }
20657         });
20658         
20659         var inputblock = input;
20660          
20661         if (this.before || this.after) {
20662             
20663             inputblock = {
20664                 cls : 'input-group',
20665                 cn :  [] 
20666             };
20667             
20668             if (this.before) {
20669                 inputblock.cn.push({
20670                     tag :'span',
20671                     cls : 'input-group-addon',
20672                     html : this.before
20673                 });
20674             }
20675             
20676             inputblock.cn.push(input);
20677             
20678             if(this.inputType != 'radio'){
20679                 inputblock.cn.push(hidden);
20680             }
20681             
20682             if (this.after) {
20683                 inputblock.cn.push({
20684                     tag :'span',
20685                     cls : 'input-group-addon',
20686                     html : this.after
20687                 });
20688             }
20689             
20690         }
20691         
20692         if (align ==='left' && this.fieldLabel.length) {
20693 //                Roo.log("left and has label");
20694             cfg.cn = [
20695                 {
20696                     tag: 'label',
20697                     'for' :  id,
20698                     cls : 'control-label',
20699                     html : this.fieldLabel
20700                 },
20701                 {
20702                     cls : "", 
20703                     cn: [
20704                         inputblock
20705                     ]
20706                 }
20707             ];
20708             
20709             if(this.labelWidth > 12){
20710                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20711             }
20712             
20713             if(this.labelWidth < 13 && this.labelmd == 0){
20714                 this.labelmd = this.labelWidth;
20715             }
20716             
20717             if(this.labellg > 0){
20718                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20719                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20720             }
20721             
20722             if(this.labelmd > 0){
20723                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20724                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20725             }
20726             
20727             if(this.labelsm > 0){
20728                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20729                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20730             }
20731             
20732             if(this.labelxs > 0){
20733                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20734                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20735             }
20736             
20737         } else if ( this.fieldLabel.length) {
20738 //                Roo.log(" label");
20739                 cfg.cn = [
20740                    
20741                     {
20742                         tag: this.boxLabel ? 'span' : 'label',
20743                         'for': id,
20744                         cls: 'control-label box-input-label',
20745                         //cls : 'input-group-addon',
20746                         html : this.fieldLabel
20747                     },
20748                     
20749                     inputblock
20750                     
20751                 ];
20752
20753         } else {
20754             
20755 //                Roo.log(" no label && no align");
20756                 cfg.cn = [  inputblock ] ;
20757                 
20758                 
20759         }
20760         
20761         if(this.boxLabel){
20762              var boxLabelCfg = {
20763                 tag: 'label',
20764                 //'for': id, // box label is handled by onclick - so no for...
20765                 cls: 'box-label',
20766                 html: this.boxLabel
20767             };
20768             
20769             if(this.tooltip){
20770                 boxLabelCfg.tooltip = this.tooltip;
20771             }
20772              
20773             cfg.cn.push(boxLabelCfg);
20774         }
20775         
20776         if(this.inputType != 'radio'){
20777             cfg.cn.push(hidden);
20778         }
20779         
20780         return cfg;
20781         
20782     },
20783     
20784     /**
20785      * return the real input element.
20786      */
20787     inputEl: function ()
20788     {
20789         return this.el.select('input.roo-' + this.inputType,true).first();
20790     },
20791     hiddenEl: function ()
20792     {
20793         return this.el.select('input.roo-hidden-value',true).first();
20794     },
20795     
20796     labelEl: function()
20797     {
20798         return this.el.select('label.control-label',true).first();
20799     },
20800     /* depricated... */
20801     
20802     label: function()
20803     {
20804         return this.labelEl();
20805     },
20806     
20807     boxLabelEl: function()
20808     {
20809         return this.el.select('label.box-label',true).first();
20810     },
20811     
20812     initEvents : function()
20813     {
20814 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20815         
20816         this.inputEl().on('click', this.onClick,  this);
20817         
20818         if (this.boxLabel) { 
20819             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20820         }
20821         
20822         this.startValue = this.getValue();
20823         
20824         if(this.groupId){
20825             Roo.bootstrap.CheckBox.register(this);
20826         }
20827     },
20828     
20829     onClick : function(e)
20830     {   
20831         if(this.fireEvent('click', this, e) !== false){
20832             this.setChecked(!this.checked);
20833         }
20834         
20835     },
20836     
20837     setChecked : function(state,suppressEvent)
20838     {
20839         this.startValue = this.getValue();
20840
20841         if(this.inputType == 'radio'){
20842             
20843             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20844                 e.dom.checked = false;
20845             });
20846             
20847             this.inputEl().dom.checked = true;
20848             
20849             this.inputEl().dom.value = this.inputValue;
20850             
20851             if(suppressEvent !== true){
20852                 this.fireEvent('check', this, true);
20853             }
20854             
20855             this.validate();
20856             
20857             return;
20858         }
20859         
20860         this.checked = state;
20861         
20862         this.inputEl().dom.checked = state;
20863         
20864         
20865         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20866         
20867         if(suppressEvent !== true){
20868             this.fireEvent('check', this, state);
20869         }
20870         
20871         this.validate();
20872     },
20873     
20874     getValue : function()
20875     {
20876         if(this.inputType == 'radio'){
20877             return this.getGroupValue();
20878         }
20879         
20880         return this.hiddenEl().dom.value;
20881         
20882     },
20883     
20884     getGroupValue : function()
20885     {
20886         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20887             return '';
20888         }
20889         
20890         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20891     },
20892     
20893     setValue : function(v,suppressEvent)
20894     {
20895         if(this.inputType == 'radio'){
20896             this.setGroupValue(v, suppressEvent);
20897             return;
20898         }
20899         
20900         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20901         
20902         this.validate();
20903     },
20904     
20905     setGroupValue : function(v, suppressEvent)
20906     {
20907         this.startValue = this.getValue();
20908         
20909         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20910             e.dom.checked = false;
20911             
20912             if(e.dom.value == v){
20913                 e.dom.checked = true;
20914             }
20915         });
20916         
20917         if(suppressEvent !== true){
20918             this.fireEvent('check', this, true);
20919         }
20920
20921         this.validate();
20922         
20923         return;
20924     },
20925     
20926     validate : function()
20927     {
20928         if(this.getVisibilityEl().hasClass('hidden')){
20929             return true;
20930         }
20931         
20932         if(
20933                 this.disabled || 
20934                 (this.inputType == 'radio' && this.validateRadio()) ||
20935                 (this.inputType == 'checkbox' && this.validateCheckbox())
20936         ){
20937             this.markValid();
20938             return true;
20939         }
20940         
20941         this.markInvalid();
20942         return false;
20943     },
20944     
20945     validateRadio : function()
20946     {
20947         if(this.getVisibilityEl().hasClass('hidden')){
20948             return true;
20949         }
20950         
20951         if(this.allowBlank){
20952             return true;
20953         }
20954         
20955         var valid = false;
20956         
20957         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20958             if(!e.dom.checked){
20959                 return;
20960             }
20961             
20962             valid = true;
20963             
20964             return false;
20965         });
20966         
20967         return valid;
20968     },
20969     
20970     validateCheckbox : function()
20971     {
20972         if(!this.groupId){
20973             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20974             //return (this.getValue() == this.inputValue) ? true : false;
20975         }
20976         
20977         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20978         
20979         if(!group){
20980             return false;
20981         }
20982         
20983         var r = false;
20984         
20985         for(var i in group){
20986             if(group[i].el.isVisible(true)){
20987                 r = false;
20988                 break;
20989             }
20990             
20991             r = true;
20992         }
20993         
20994         for(var i in group){
20995             if(r){
20996                 break;
20997             }
20998             
20999             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21000         }
21001         
21002         return r;
21003     },
21004     
21005     /**
21006      * Mark this field as valid
21007      */
21008     markValid : function()
21009     {
21010         var _this = this;
21011         
21012         this.fireEvent('valid', this);
21013         
21014         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21015         
21016         if(this.groupId){
21017             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21018         }
21019         
21020         if(label){
21021             label.markValid();
21022         }
21023
21024         if(this.inputType == 'radio'){
21025             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21026                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21027                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21028             });
21029             
21030             return;
21031         }
21032
21033         if(!this.groupId){
21034             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21035             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21036             return;
21037         }
21038         
21039         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21040         
21041         if(!group){
21042             return;
21043         }
21044         
21045         for(var i in group){
21046             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21047             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21048         }
21049     },
21050     
21051      /**
21052      * Mark this field as invalid
21053      * @param {String} msg The validation message
21054      */
21055     markInvalid : function(msg)
21056     {
21057         if(this.allowBlank){
21058             return;
21059         }
21060         
21061         var _this = this;
21062         
21063         this.fireEvent('invalid', this, msg);
21064         
21065         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21066         
21067         if(this.groupId){
21068             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21069         }
21070         
21071         if(label){
21072             label.markInvalid();
21073         }
21074             
21075         if(this.inputType == 'radio'){
21076             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21077                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21078                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21079             });
21080             
21081             return;
21082         }
21083         
21084         if(!this.groupId){
21085             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21086             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21087             return;
21088         }
21089         
21090         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21091         
21092         if(!group){
21093             return;
21094         }
21095         
21096         for(var i in group){
21097             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21098             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21099         }
21100         
21101     },
21102     
21103     clearInvalid : function()
21104     {
21105         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21106         
21107         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21108         
21109         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21110         
21111         if (label && label.iconEl) {
21112             label.iconEl.removeClass(label.validClass);
21113             label.iconEl.removeClass(label.invalidClass);
21114         }
21115     },
21116     
21117     disable : function()
21118     {
21119         if(this.inputType != 'radio'){
21120             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21121             return;
21122         }
21123         
21124         var _this = this;
21125         
21126         if(this.rendered){
21127             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21128                 _this.getActionEl().addClass(this.disabledClass);
21129                 e.dom.disabled = true;
21130             });
21131         }
21132         
21133         this.disabled = true;
21134         this.fireEvent("disable", this);
21135         return this;
21136     },
21137
21138     enable : function()
21139     {
21140         if(this.inputType != 'radio'){
21141             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21142             return;
21143         }
21144         
21145         var _this = this;
21146         
21147         if(this.rendered){
21148             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21149                 _this.getActionEl().removeClass(this.disabledClass);
21150                 e.dom.disabled = false;
21151             });
21152         }
21153         
21154         this.disabled = false;
21155         this.fireEvent("enable", this);
21156         return this;
21157     },
21158     
21159     setBoxLabel : function(v)
21160     {
21161         this.boxLabel = v;
21162         
21163         if(this.rendered){
21164             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21165         }
21166     }
21167
21168 });
21169
21170 Roo.apply(Roo.bootstrap.CheckBox, {
21171     
21172     groups: {},
21173     
21174      /**
21175     * register a CheckBox Group
21176     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21177     */
21178     register : function(checkbox)
21179     {
21180         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21181             this.groups[checkbox.groupId] = {};
21182         }
21183         
21184         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21185             return;
21186         }
21187         
21188         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21189         
21190     },
21191     /**
21192     * fetch a CheckBox Group based on the group ID
21193     * @param {string} the group ID
21194     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21195     */
21196     get: function(groupId) {
21197         if (typeof(this.groups[groupId]) == 'undefined') {
21198             return false;
21199         }
21200         
21201         return this.groups[groupId] ;
21202     }
21203     
21204     
21205 });
21206 /*
21207  * - LGPL
21208  *
21209  * RadioItem
21210  * 
21211  */
21212
21213 /**
21214  * @class Roo.bootstrap.Radio
21215  * @extends Roo.bootstrap.Component
21216  * Bootstrap Radio class
21217  * @cfg {String} boxLabel - the label associated
21218  * @cfg {String} value - the value of radio
21219  * 
21220  * @constructor
21221  * Create a new Radio
21222  * @param {Object} config The config object
21223  */
21224 Roo.bootstrap.Radio = function(config){
21225     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21226     
21227 };
21228
21229 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21230     
21231     boxLabel : '',
21232     
21233     value : '',
21234     
21235     getAutoCreate : function()
21236     {
21237         var cfg = {
21238             tag : 'div',
21239             cls : 'form-group radio',
21240             cn : [
21241                 {
21242                     tag : 'label',
21243                     cls : 'box-label',
21244                     html : this.boxLabel
21245                 }
21246             ]
21247         };
21248         
21249         return cfg;
21250     },
21251     
21252     initEvents : function() 
21253     {
21254         this.parent().register(this);
21255         
21256         this.el.on('click', this.onClick, this);
21257         
21258     },
21259     
21260     onClick : function(e)
21261     {
21262         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21263             this.setChecked(true);
21264         }
21265     },
21266     
21267     setChecked : function(state, suppressEvent)
21268     {
21269         this.parent().setValue(this.value, suppressEvent);
21270         
21271     },
21272     
21273     setBoxLabel : function(v)
21274     {
21275         this.boxLabel = v;
21276         
21277         if(this.rendered){
21278             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21279         }
21280     }
21281     
21282 });
21283  
21284
21285  /*
21286  * - LGPL
21287  *
21288  * Input
21289  * 
21290  */
21291
21292 /**
21293  * @class Roo.bootstrap.SecurePass
21294  * @extends Roo.bootstrap.Input
21295  * Bootstrap SecurePass class
21296  *
21297  * 
21298  * @constructor
21299  * Create a new SecurePass
21300  * @param {Object} config The config object
21301  */
21302  
21303 Roo.bootstrap.SecurePass = function (config) {
21304     // these go here, so the translation tool can replace them..
21305     this.errors = {
21306         PwdEmpty: "Please type a password, and then retype it to confirm.",
21307         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21308         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21309         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21310         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21311         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21312         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21313         TooWeak: "Your password is Too Weak."
21314     },
21315     this.meterLabel = "Password strength:";
21316     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21317     this.meterClass = [
21318         "roo-password-meter-tooweak", 
21319         "roo-password-meter-weak", 
21320         "roo-password-meter-medium", 
21321         "roo-password-meter-strong", 
21322         "roo-password-meter-grey"
21323     ];
21324     
21325     this.errors = {};
21326     
21327     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21328 }
21329
21330 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21331     /**
21332      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21333      * {
21334      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21335      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21336      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21337      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21338      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21339      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21340      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21341      * })
21342      */
21343     // private
21344     
21345     meterWidth: 300,
21346     errorMsg :'',    
21347     errors: false,
21348     imageRoot: '/',
21349     /**
21350      * @cfg {String/Object} Label for the strength meter (defaults to
21351      * 'Password strength:')
21352      */
21353     // private
21354     meterLabel: '',
21355     /**
21356      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21357      * ['Weak', 'Medium', 'Strong'])
21358      */
21359     // private    
21360     pwdStrengths: false,    
21361     // private
21362     strength: 0,
21363     // private
21364     _lastPwd: null,
21365     // private
21366     kCapitalLetter: 0,
21367     kSmallLetter: 1,
21368     kDigit: 2,
21369     kPunctuation: 3,
21370     
21371     insecure: false,
21372     // private
21373     initEvents: function ()
21374     {
21375         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21376
21377         if (this.el.is('input[type=password]') && Roo.isSafari) {
21378             this.el.on('keydown', this.SafariOnKeyDown, this);
21379         }
21380
21381         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21382     },
21383     // private
21384     onRender: function (ct, position)
21385     {
21386         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21387         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21388         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21389
21390         this.trigger.createChild({
21391                    cn: [
21392                     {
21393                     //id: 'PwdMeter',
21394                     tag: 'div',
21395                     cls: 'roo-password-meter-grey col-xs-12',
21396                     style: {
21397                         //width: 0,
21398                         //width: this.meterWidth + 'px'                                                
21399                         }
21400                     },
21401                     {                            
21402                          cls: 'roo-password-meter-text'                          
21403                     }
21404                 ]            
21405         });
21406
21407          
21408         if (this.hideTrigger) {
21409             this.trigger.setDisplayed(false);
21410         }
21411         this.setSize(this.width || '', this.height || '');
21412     },
21413     // private
21414     onDestroy: function ()
21415     {
21416         if (this.trigger) {
21417             this.trigger.removeAllListeners();
21418             this.trigger.remove();
21419         }
21420         if (this.wrap) {
21421             this.wrap.remove();
21422         }
21423         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21424     },
21425     // private
21426     checkStrength: function ()
21427     {
21428         var pwd = this.inputEl().getValue();
21429         if (pwd == this._lastPwd) {
21430             return;
21431         }
21432
21433         var strength;
21434         if (this.ClientSideStrongPassword(pwd)) {
21435             strength = 3;
21436         } else if (this.ClientSideMediumPassword(pwd)) {
21437             strength = 2;
21438         } else if (this.ClientSideWeakPassword(pwd)) {
21439             strength = 1;
21440         } else {
21441             strength = 0;
21442         }
21443         
21444         Roo.log('strength1: ' + strength);
21445         
21446         //var pm = this.trigger.child('div/div/div').dom;
21447         var pm = this.trigger.child('div/div');
21448         pm.removeClass(this.meterClass);
21449         pm.addClass(this.meterClass[strength]);
21450                 
21451         
21452         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21453                 
21454         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21455         
21456         this._lastPwd = pwd;
21457     },
21458     reset: function ()
21459     {
21460         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21461         
21462         this._lastPwd = '';
21463         
21464         var pm = this.trigger.child('div/div');
21465         pm.removeClass(this.meterClass);
21466         pm.addClass('roo-password-meter-grey');        
21467         
21468         
21469         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21470         
21471         pt.innerHTML = '';
21472         this.inputEl().dom.type='password';
21473     },
21474     // private
21475     validateValue: function (value)
21476     {
21477         
21478         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21479             return false;
21480         }
21481         if (value.length == 0) {
21482             if (this.allowBlank) {
21483                 this.clearInvalid();
21484                 return true;
21485             }
21486
21487             this.markInvalid(this.errors.PwdEmpty);
21488             this.errorMsg = this.errors.PwdEmpty;
21489             return false;
21490         }
21491         
21492         if(this.insecure){
21493             return true;
21494         }
21495         
21496         if ('[\x21-\x7e]*'.match(value)) {
21497             this.markInvalid(this.errors.PwdBadChar);
21498             this.errorMsg = this.errors.PwdBadChar;
21499             return false;
21500         }
21501         if (value.length < 6) {
21502             this.markInvalid(this.errors.PwdShort);
21503             this.errorMsg = this.errors.PwdShort;
21504             return false;
21505         }
21506         if (value.length > 16) {
21507             this.markInvalid(this.errors.PwdLong);
21508             this.errorMsg = this.errors.PwdLong;
21509             return false;
21510         }
21511         var strength;
21512         if (this.ClientSideStrongPassword(value)) {
21513             strength = 3;
21514         } else if (this.ClientSideMediumPassword(value)) {
21515             strength = 2;
21516         } else if (this.ClientSideWeakPassword(value)) {
21517             strength = 1;
21518         } else {
21519             strength = 0;
21520         }
21521
21522         
21523         if (strength < 2) {
21524             //this.markInvalid(this.errors.TooWeak);
21525             this.errorMsg = this.errors.TooWeak;
21526             //return false;
21527         }
21528         
21529         
21530         console.log('strength2: ' + strength);
21531         
21532         //var pm = this.trigger.child('div/div/div').dom;
21533         
21534         var pm = this.trigger.child('div/div');
21535         pm.removeClass(this.meterClass);
21536         pm.addClass(this.meterClass[strength]);
21537                 
21538         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21539                 
21540         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21541         
21542         this.errorMsg = ''; 
21543         return true;
21544     },
21545     // private
21546     CharacterSetChecks: function (type)
21547     {
21548         this.type = type;
21549         this.fResult = false;
21550     },
21551     // private
21552     isctype: function (character, type)
21553     {
21554         switch (type) {  
21555             case this.kCapitalLetter:
21556                 if (character >= 'A' && character <= 'Z') {
21557                     return true;
21558                 }
21559                 break;
21560             
21561             case this.kSmallLetter:
21562                 if (character >= 'a' && character <= 'z') {
21563                     return true;
21564                 }
21565                 break;
21566             
21567             case this.kDigit:
21568                 if (character >= '0' && character <= '9') {
21569                     return true;
21570                 }
21571                 break;
21572             
21573             case this.kPunctuation:
21574                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21575                     return true;
21576                 }
21577                 break;
21578             
21579             default:
21580                 return false;
21581         }
21582
21583     },
21584     // private
21585     IsLongEnough: function (pwd, size)
21586     {
21587         return !(pwd == null || isNaN(size) || pwd.length < size);
21588     },
21589     // private
21590     SpansEnoughCharacterSets: function (word, nb)
21591     {
21592         if (!this.IsLongEnough(word, nb))
21593         {
21594             return false;
21595         }
21596
21597         var characterSetChecks = new Array(
21598             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21599             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21600         );
21601         
21602         for (var index = 0; index < word.length; ++index) {
21603             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21604                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21605                     characterSetChecks[nCharSet].fResult = true;
21606                     break;
21607                 }
21608             }
21609         }
21610
21611         var nCharSets = 0;
21612         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21613             if (characterSetChecks[nCharSet].fResult) {
21614                 ++nCharSets;
21615             }
21616         }
21617
21618         if (nCharSets < nb) {
21619             return false;
21620         }
21621         return true;
21622     },
21623     // private
21624     ClientSideStrongPassword: function (pwd)
21625     {
21626         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21627     },
21628     // private
21629     ClientSideMediumPassword: function (pwd)
21630     {
21631         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21632     },
21633     // private
21634     ClientSideWeakPassword: function (pwd)
21635     {
21636         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21637     }
21638           
21639 })//<script type="text/javascript">
21640
21641 /*
21642  * Based  Ext JS Library 1.1.1
21643  * Copyright(c) 2006-2007, Ext JS, LLC.
21644  * LGPL
21645  *
21646  */
21647  
21648 /**
21649  * @class Roo.HtmlEditorCore
21650  * @extends Roo.Component
21651  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21652  *
21653  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21654  */
21655
21656 Roo.HtmlEditorCore = function(config){
21657     
21658     
21659     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21660     
21661     
21662     this.addEvents({
21663         /**
21664          * @event initialize
21665          * Fires when the editor is fully initialized (including the iframe)
21666          * @param {Roo.HtmlEditorCore} this
21667          */
21668         initialize: true,
21669         /**
21670          * @event activate
21671          * Fires when the editor is first receives the focus. Any insertion must wait
21672          * until after this event.
21673          * @param {Roo.HtmlEditorCore} this
21674          */
21675         activate: true,
21676          /**
21677          * @event beforesync
21678          * Fires before the textarea is updated with content from the editor iframe. Return false
21679          * to cancel the sync.
21680          * @param {Roo.HtmlEditorCore} this
21681          * @param {String} html
21682          */
21683         beforesync: true,
21684          /**
21685          * @event beforepush
21686          * Fires before the iframe editor is updated with content from the textarea. Return false
21687          * to cancel the push.
21688          * @param {Roo.HtmlEditorCore} this
21689          * @param {String} html
21690          */
21691         beforepush: true,
21692          /**
21693          * @event sync
21694          * Fires when the textarea is updated with content from the editor iframe.
21695          * @param {Roo.HtmlEditorCore} this
21696          * @param {String} html
21697          */
21698         sync: true,
21699          /**
21700          * @event push
21701          * Fires when the iframe editor is updated with content from the textarea.
21702          * @param {Roo.HtmlEditorCore} this
21703          * @param {String} html
21704          */
21705         push: true,
21706         
21707         /**
21708          * @event editorevent
21709          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21710          * @param {Roo.HtmlEditorCore} this
21711          */
21712         editorevent: true
21713         
21714     });
21715     
21716     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21717     
21718     // defaults : white / black...
21719     this.applyBlacklists();
21720     
21721     
21722     
21723 };
21724
21725
21726 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21727
21728
21729      /**
21730      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21731      */
21732     
21733     owner : false,
21734     
21735      /**
21736      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21737      *                        Roo.resizable.
21738      */
21739     resizable : false,
21740      /**
21741      * @cfg {Number} height (in pixels)
21742      */   
21743     height: 300,
21744    /**
21745      * @cfg {Number} width (in pixels)
21746      */   
21747     width: 500,
21748     
21749     /**
21750      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21751      * 
21752      */
21753     stylesheets: false,
21754     
21755     // id of frame..
21756     frameId: false,
21757     
21758     // private properties
21759     validationEvent : false,
21760     deferHeight: true,
21761     initialized : false,
21762     activated : false,
21763     sourceEditMode : false,
21764     onFocus : Roo.emptyFn,
21765     iframePad:3,
21766     hideMode:'offsets',
21767     
21768     clearUp: true,
21769     
21770     // blacklist + whitelisted elements..
21771     black: false,
21772     white: false,
21773      
21774     bodyCls : '',
21775
21776     /**
21777      * Protected method that will not generally be called directly. It
21778      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21779      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21780      */
21781     getDocMarkup : function(){
21782         // body styles..
21783         var st = '';
21784         
21785         // inherit styels from page...?? 
21786         if (this.stylesheets === false) {
21787             
21788             Roo.get(document.head).select('style').each(function(node) {
21789                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21790             });
21791             
21792             Roo.get(document.head).select('link').each(function(node) { 
21793                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21794             });
21795             
21796         } else if (!this.stylesheets.length) {
21797                 // simple..
21798                 st = '<style type="text/css">' +
21799                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21800                    '</style>';
21801         } else { 
21802             st = '<style type="text/css">' +
21803                     this.stylesheets +
21804                 '</style>';
21805         }
21806         
21807         st +=  '<style type="text/css">' +
21808             'IMG { cursor: pointer } ' +
21809         '</style>';
21810
21811         var cls = 'roo-htmleditor-body';
21812         
21813         if(this.bodyCls.length){
21814             cls += ' ' + this.bodyCls;
21815         }
21816         
21817         return '<html><head>' + st  +
21818             //<style type="text/css">' +
21819             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21820             //'</style>' +
21821             ' </head><body class="' +  cls + '"></body></html>';
21822     },
21823
21824     // private
21825     onRender : function(ct, position)
21826     {
21827         var _t = this;
21828         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21829         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21830         
21831         
21832         this.el.dom.style.border = '0 none';
21833         this.el.dom.setAttribute('tabIndex', -1);
21834         this.el.addClass('x-hidden hide');
21835         
21836         
21837         
21838         if(Roo.isIE){ // fix IE 1px bogus margin
21839             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21840         }
21841        
21842         
21843         this.frameId = Roo.id();
21844         
21845          
21846         
21847         var iframe = this.owner.wrap.createChild({
21848             tag: 'iframe',
21849             cls: 'form-control', // bootstrap..
21850             id: this.frameId,
21851             name: this.frameId,
21852             frameBorder : 'no',
21853             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21854         }, this.el
21855         );
21856         
21857         
21858         this.iframe = iframe.dom;
21859
21860          this.assignDocWin();
21861         
21862         this.doc.designMode = 'on';
21863        
21864         this.doc.open();
21865         this.doc.write(this.getDocMarkup());
21866         this.doc.close();
21867
21868         
21869         var task = { // must defer to wait for browser to be ready
21870             run : function(){
21871                 //console.log("run task?" + this.doc.readyState);
21872                 this.assignDocWin();
21873                 if(this.doc.body || this.doc.readyState == 'complete'){
21874                     try {
21875                         this.doc.designMode="on";
21876                     } catch (e) {
21877                         return;
21878                     }
21879                     Roo.TaskMgr.stop(task);
21880                     this.initEditor.defer(10, this);
21881                 }
21882             },
21883             interval : 10,
21884             duration: 10000,
21885             scope: this
21886         };
21887         Roo.TaskMgr.start(task);
21888
21889     },
21890
21891     // private
21892     onResize : function(w, h)
21893     {
21894          Roo.log('resize: ' +w + ',' + h );
21895         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21896         if(!this.iframe){
21897             return;
21898         }
21899         if(typeof w == 'number'){
21900             
21901             this.iframe.style.width = w + 'px';
21902         }
21903         if(typeof h == 'number'){
21904             
21905             this.iframe.style.height = h + 'px';
21906             if(this.doc){
21907                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21908             }
21909         }
21910         
21911     },
21912
21913     /**
21914      * Toggles the editor between standard and source edit mode.
21915      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21916      */
21917     toggleSourceEdit : function(sourceEditMode){
21918         
21919         this.sourceEditMode = sourceEditMode === true;
21920         
21921         if(this.sourceEditMode){
21922  
21923             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21924             
21925         }else{
21926             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21927             //this.iframe.className = '';
21928             this.deferFocus();
21929         }
21930         //this.setSize(this.owner.wrap.getSize());
21931         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21932     },
21933
21934     
21935   
21936
21937     /**
21938      * Protected method that will not generally be called directly. If you need/want
21939      * custom HTML cleanup, this is the method you should override.
21940      * @param {String} html The HTML to be cleaned
21941      * return {String} The cleaned HTML
21942      */
21943     cleanHtml : function(html){
21944         html = String(html);
21945         if(html.length > 5){
21946             if(Roo.isSafari){ // strip safari nonsense
21947                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21948             }
21949         }
21950         if(html == '&nbsp;'){
21951             html = '';
21952         }
21953         return html;
21954     },
21955
21956     /**
21957      * HTML Editor -> Textarea
21958      * Protected method that will not generally be called directly. Syncs the contents
21959      * of the editor iframe with the textarea.
21960      */
21961     syncValue : function(){
21962         if(this.initialized){
21963             var bd = (this.doc.body || this.doc.documentElement);
21964             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21965             var html = bd.innerHTML;
21966             if(Roo.isSafari){
21967                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21968                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21969                 if(m && m[1]){
21970                     html = '<div style="'+m[0]+'">' + html + '</div>';
21971                 }
21972             }
21973             html = this.cleanHtml(html);
21974             // fix up the special chars.. normaly like back quotes in word...
21975             // however we do not want to do this with chinese..
21976             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21977                 var cc = b.charCodeAt();
21978                 if (
21979                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21980                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21981                     (cc >= 0xf900 && cc < 0xfb00 )
21982                 ) {
21983                         return b;
21984                 }
21985                 return "&#"+cc+";" 
21986             });
21987             if(this.owner.fireEvent('beforesync', this, html) !== false){
21988                 this.el.dom.value = html;
21989                 this.owner.fireEvent('sync', this, html);
21990             }
21991         }
21992     },
21993
21994     /**
21995      * Protected method that will not generally be called directly. Pushes the value of the textarea
21996      * into the iframe editor.
21997      */
21998     pushValue : function(){
21999         if(this.initialized){
22000             var v = this.el.dom.value.trim();
22001             
22002 //            if(v.length < 1){
22003 //                v = '&#160;';
22004 //            }
22005             
22006             if(this.owner.fireEvent('beforepush', this, v) !== false){
22007                 var d = (this.doc.body || this.doc.documentElement);
22008                 d.innerHTML = v;
22009                 this.cleanUpPaste();
22010                 this.el.dom.value = d.innerHTML;
22011                 this.owner.fireEvent('push', this, v);
22012             }
22013         }
22014     },
22015
22016     // private
22017     deferFocus : function(){
22018         this.focus.defer(10, this);
22019     },
22020
22021     // doc'ed in Field
22022     focus : function(){
22023         if(this.win && !this.sourceEditMode){
22024             this.win.focus();
22025         }else{
22026             this.el.focus();
22027         }
22028     },
22029     
22030     assignDocWin: function()
22031     {
22032         var iframe = this.iframe;
22033         
22034          if(Roo.isIE){
22035             this.doc = iframe.contentWindow.document;
22036             this.win = iframe.contentWindow;
22037         } else {
22038 //            if (!Roo.get(this.frameId)) {
22039 //                return;
22040 //            }
22041 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22042 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22043             
22044             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22045                 return;
22046             }
22047             
22048             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22049             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22050         }
22051     },
22052     
22053     // private
22054     initEditor : function(){
22055         //console.log("INIT EDITOR");
22056         this.assignDocWin();
22057         
22058         
22059         
22060         this.doc.designMode="on";
22061         this.doc.open();
22062         this.doc.write(this.getDocMarkup());
22063         this.doc.close();
22064         
22065         var dbody = (this.doc.body || this.doc.documentElement);
22066         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22067         // this copies styles from the containing element into thsi one..
22068         // not sure why we need all of this..
22069         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22070         
22071         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22072         //ss['background-attachment'] = 'fixed'; // w3c
22073         dbody.bgProperties = 'fixed'; // ie
22074         //Roo.DomHelper.applyStyles(dbody, ss);
22075         Roo.EventManager.on(this.doc, {
22076             //'mousedown': this.onEditorEvent,
22077             'mouseup': this.onEditorEvent,
22078             'dblclick': this.onEditorEvent,
22079             'click': this.onEditorEvent,
22080             'keyup': this.onEditorEvent,
22081             buffer:100,
22082             scope: this
22083         });
22084         if(Roo.isGecko){
22085             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22086         }
22087         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22088             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22089         }
22090         this.initialized = true;
22091
22092         this.owner.fireEvent('initialize', this);
22093         this.pushValue();
22094     },
22095
22096     // private
22097     onDestroy : function(){
22098         
22099         
22100         
22101         if(this.rendered){
22102             
22103             //for (var i =0; i < this.toolbars.length;i++) {
22104             //    // fixme - ask toolbars for heights?
22105             //    this.toolbars[i].onDestroy();
22106            // }
22107             
22108             //this.wrap.dom.innerHTML = '';
22109             //this.wrap.remove();
22110         }
22111     },
22112
22113     // private
22114     onFirstFocus : function(){
22115         
22116         this.assignDocWin();
22117         
22118         
22119         this.activated = true;
22120          
22121     
22122         if(Roo.isGecko){ // prevent silly gecko errors
22123             this.win.focus();
22124             var s = this.win.getSelection();
22125             if(!s.focusNode || s.focusNode.nodeType != 3){
22126                 var r = s.getRangeAt(0);
22127                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22128                 r.collapse(true);
22129                 this.deferFocus();
22130             }
22131             try{
22132                 this.execCmd('useCSS', true);
22133                 this.execCmd('styleWithCSS', false);
22134             }catch(e){}
22135         }
22136         this.owner.fireEvent('activate', this);
22137     },
22138
22139     // private
22140     adjustFont: function(btn){
22141         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22142         //if(Roo.isSafari){ // safari
22143         //    adjust *= 2;
22144        // }
22145         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22146         if(Roo.isSafari){ // safari
22147             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22148             v =  (v < 10) ? 10 : v;
22149             v =  (v > 48) ? 48 : v;
22150             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22151             
22152         }
22153         
22154         
22155         v = Math.max(1, v+adjust);
22156         
22157         this.execCmd('FontSize', v  );
22158     },
22159
22160     onEditorEvent : function(e)
22161     {
22162         this.owner.fireEvent('editorevent', this, e);
22163       //  this.updateToolbar();
22164         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22165     },
22166
22167     insertTag : function(tg)
22168     {
22169         // could be a bit smarter... -> wrap the current selected tRoo..
22170         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22171             
22172             range = this.createRange(this.getSelection());
22173             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22174             wrappingNode.appendChild(range.extractContents());
22175             range.insertNode(wrappingNode);
22176
22177             return;
22178             
22179             
22180             
22181         }
22182         this.execCmd("formatblock",   tg);
22183         
22184     },
22185     
22186     insertText : function(txt)
22187     {
22188         
22189         
22190         var range = this.createRange();
22191         range.deleteContents();
22192                //alert(Sender.getAttribute('label'));
22193                
22194         range.insertNode(this.doc.createTextNode(txt));
22195     } ,
22196     
22197      
22198
22199     /**
22200      * Executes a Midas editor command on the editor document and performs necessary focus and
22201      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22202      * @param {String} cmd The Midas command
22203      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22204      */
22205     relayCmd : function(cmd, value){
22206         this.win.focus();
22207         this.execCmd(cmd, value);
22208         this.owner.fireEvent('editorevent', this);
22209         //this.updateToolbar();
22210         this.owner.deferFocus();
22211     },
22212
22213     /**
22214      * Executes a Midas editor command directly on the editor document.
22215      * For visual commands, you should use {@link #relayCmd} instead.
22216      * <b>This should only be called after the editor is initialized.</b>
22217      * @param {String} cmd The Midas command
22218      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22219      */
22220     execCmd : function(cmd, value){
22221         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22222         this.syncValue();
22223     },
22224  
22225  
22226    
22227     /**
22228      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22229      * to insert tRoo.
22230      * @param {String} text | dom node.. 
22231      */
22232     insertAtCursor : function(text)
22233     {
22234         
22235         if(!this.activated){
22236             return;
22237         }
22238         /*
22239         if(Roo.isIE){
22240             this.win.focus();
22241             var r = this.doc.selection.createRange();
22242             if(r){
22243                 r.collapse(true);
22244                 r.pasteHTML(text);
22245                 this.syncValue();
22246                 this.deferFocus();
22247             
22248             }
22249             return;
22250         }
22251         */
22252         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22253             this.win.focus();
22254             
22255             
22256             // from jquery ui (MIT licenced)
22257             var range, node;
22258             var win = this.win;
22259             
22260             if (win.getSelection && win.getSelection().getRangeAt) {
22261                 range = win.getSelection().getRangeAt(0);
22262                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22263                 range.insertNode(node);
22264             } else if (win.document.selection && win.document.selection.createRange) {
22265                 // no firefox support
22266                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22267                 win.document.selection.createRange().pasteHTML(txt);
22268             } else {
22269                 // no firefox support
22270                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22271                 this.execCmd('InsertHTML', txt);
22272             } 
22273             
22274             this.syncValue();
22275             
22276             this.deferFocus();
22277         }
22278     },
22279  // private
22280     mozKeyPress : function(e){
22281         if(e.ctrlKey){
22282             var c = e.getCharCode(), cmd;
22283           
22284             if(c > 0){
22285                 c = String.fromCharCode(c).toLowerCase();
22286                 switch(c){
22287                     case 'b':
22288                         cmd = 'bold';
22289                         break;
22290                     case 'i':
22291                         cmd = 'italic';
22292                         break;
22293                     
22294                     case 'u':
22295                         cmd = 'underline';
22296                         break;
22297                     
22298                     case 'v':
22299                         this.cleanUpPaste.defer(100, this);
22300                         return;
22301                         
22302                 }
22303                 if(cmd){
22304                     this.win.focus();
22305                     this.execCmd(cmd);
22306                     this.deferFocus();
22307                     e.preventDefault();
22308                 }
22309                 
22310             }
22311         }
22312     },
22313
22314     // private
22315     fixKeys : function(){ // load time branching for fastest keydown performance
22316         if(Roo.isIE){
22317             return function(e){
22318                 var k = e.getKey(), r;
22319                 if(k == e.TAB){
22320                     e.stopEvent();
22321                     r = this.doc.selection.createRange();
22322                     if(r){
22323                         r.collapse(true);
22324                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22325                         this.deferFocus();
22326                     }
22327                     return;
22328                 }
22329                 
22330                 if(k == e.ENTER){
22331                     r = this.doc.selection.createRange();
22332                     if(r){
22333                         var target = r.parentElement();
22334                         if(!target || target.tagName.toLowerCase() != 'li'){
22335                             e.stopEvent();
22336                             r.pasteHTML('<br />');
22337                             r.collapse(false);
22338                             r.select();
22339                         }
22340                     }
22341                 }
22342                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22343                     this.cleanUpPaste.defer(100, this);
22344                     return;
22345                 }
22346                 
22347                 
22348             };
22349         }else if(Roo.isOpera){
22350             return function(e){
22351                 var k = e.getKey();
22352                 if(k == e.TAB){
22353                     e.stopEvent();
22354                     this.win.focus();
22355                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22356                     this.deferFocus();
22357                 }
22358                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22359                     this.cleanUpPaste.defer(100, this);
22360                     return;
22361                 }
22362                 
22363             };
22364         }else if(Roo.isSafari){
22365             return function(e){
22366                 var k = e.getKey();
22367                 
22368                 if(k == e.TAB){
22369                     e.stopEvent();
22370                     this.execCmd('InsertText','\t');
22371                     this.deferFocus();
22372                     return;
22373                 }
22374                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22375                     this.cleanUpPaste.defer(100, this);
22376                     return;
22377                 }
22378                 
22379              };
22380         }
22381     }(),
22382     
22383     getAllAncestors: function()
22384     {
22385         var p = this.getSelectedNode();
22386         var a = [];
22387         if (!p) {
22388             a.push(p); // push blank onto stack..
22389             p = this.getParentElement();
22390         }
22391         
22392         
22393         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22394             a.push(p);
22395             p = p.parentNode;
22396         }
22397         a.push(this.doc.body);
22398         return a;
22399     },
22400     lastSel : false,
22401     lastSelNode : false,
22402     
22403     
22404     getSelection : function() 
22405     {
22406         this.assignDocWin();
22407         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22408     },
22409     
22410     getSelectedNode: function() 
22411     {
22412         // this may only work on Gecko!!!
22413         
22414         // should we cache this!!!!
22415         
22416         
22417         
22418          
22419         var range = this.createRange(this.getSelection()).cloneRange();
22420         
22421         if (Roo.isIE) {
22422             var parent = range.parentElement();
22423             while (true) {
22424                 var testRange = range.duplicate();
22425                 testRange.moveToElementText(parent);
22426                 if (testRange.inRange(range)) {
22427                     break;
22428                 }
22429                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22430                     break;
22431                 }
22432                 parent = parent.parentElement;
22433             }
22434             return parent;
22435         }
22436         
22437         // is ancestor a text element.
22438         var ac =  range.commonAncestorContainer;
22439         if (ac.nodeType == 3) {
22440             ac = ac.parentNode;
22441         }
22442         
22443         var ar = ac.childNodes;
22444          
22445         var nodes = [];
22446         var other_nodes = [];
22447         var has_other_nodes = false;
22448         for (var i=0;i<ar.length;i++) {
22449             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22450                 continue;
22451             }
22452             // fullly contained node.
22453             
22454             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22455                 nodes.push(ar[i]);
22456                 continue;
22457             }
22458             
22459             // probably selected..
22460             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22461                 other_nodes.push(ar[i]);
22462                 continue;
22463             }
22464             // outer..
22465             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22466                 continue;
22467             }
22468             
22469             
22470             has_other_nodes = true;
22471         }
22472         if (!nodes.length && other_nodes.length) {
22473             nodes= other_nodes;
22474         }
22475         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22476             return false;
22477         }
22478         
22479         return nodes[0];
22480     },
22481     createRange: function(sel)
22482     {
22483         // this has strange effects when using with 
22484         // top toolbar - not sure if it's a great idea.
22485         //this.editor.contentWindow.focus();
22486         if (typeof sel != "undefined") {
22487             try {
22488                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22489             } catch(e) {
22490                 return this.doc.createRange();
22491             }
22492         } else {
22493             return this.doc.createRange();
22494         }
22495     },
22496     getParentElement: function()
22497     {
22498         
22499         this.assignDocWin();
22500         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22501         
22502         var range = this.createRange(sel);
22503          
22504         try {
22505             var p = range.commonAncestorContainer;
22506             while (p.nodeType == 3) { // text node
22507                 p = p.parentNode;
22508             }
22509             return p;
22510         } catch (e) {
22511             return null;
22512         }
22513     
22514     },
22515     /***
22516      *
22517      * Range intersection.. the hard stuff...
22518      *  '-1' = before
22519      *  '0' = hits..
22520      *  '1' = after.
22521      *         [ -- selected range --- ]
22522      *   [fail]                        [fail]
22523      *
22524      *    basically..
22525      *      if end is before start or  hits it. fail.
22526      *      if start is after end or hits it fail.
22527      *
22528      *   if either hits (but other is outside. - then it's not 
22529      *   
22530      *    
22531      **/
22532     
22533     
22534     // @see http://www.thismuchiknow.co.uk/?p=64.
22535     rangeIntersectsNode : function(range, node)
22536     {
22537         var nodeRange = node.ownerDocument.createRange();
22538         try {
22539             nodeRange.selectNode(node);
22540         } catch (e) {
22541             nodeRange.selectNodeContents(node);
22542         }
22543     
22544         var rangeStartRange = range.cloneRange();
22545         rangeStartRange.collapse(true);
22546     
22547         var rangeEndRange = range.cloneRange();
22548         rangeEndRange.collapse(false);
22549     
22550         var nodeStartRange = nodeRange.cloneRange();
22551         nodeStartRange.collapse(true);
22552     
22553         var nodeEndRange = nodeRange.cloneRange();
22554         nodeEndRange.collapse(false);
22555     
22556         return rangeStartRange.compareBoundaryPoints(
22557                  Range.START_TO_START, nodeEndRange) == -1 &&
22558                rangeEndRange.compareBoundaryPoints(
22559                  Range.START_TO_START, nodeStartRange) == 1;
22560         
22561          
22562     },
22563     rangeCompareNode : function(range, node)
22564     {
22565         var nodeRange = node.ownerDocument.createRange();
22566         try {
22567             nodeRange.selectNode(node);
22568         } catch (e) {
22569             nodeRange.selectNodeContents(node);
22570         }
22571         
22572         
22573         range.collapse(true);
22574     
22575         nodeRange.collapse(true);
22576      
22577         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22578         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22579          
22580         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22581         
22582         var nodeIsBefore   =  ss == 1;
22583         var nodeIsAfter    = ee == -1;
22584         
22585         if (nodeIsBefore && nodeIsAfter) {
22586             return 0; // outer
22587         }
22588         if (!nodeIsBefore && nodeIsAfter) {
22589             return 1; //right trailed.
22590         }
22591         
22592         if (nodeIsBefore && !nodeIsAfter) {
22593             return 2;  // left trailed.
22594         }
22595         // fully contined.
22596         return 3;
22597     },
22598
22599     // private? - in a new class?
22600     cleanUpPaste :  function()
22601     {
22602         // cleans up the whole document..
22603         Roo.log('cleanuppaste');
22604         
22605         this.cleanUpChildren(this.doc.body);
22606         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22607         if (clean != this.doc.body.innerHTML) {
22608             this.doc.body.innerHTML = clean;
22609         }
22610         
22611     },
22612     
22613     cleanWordChars : function(input) {// change the chars to hex code
22614         var he = Roo.HtmlEditorCore;
22615         
22616         var output = input;
22617         Roo.each(he.swapCodes, function(sw) { 
22618             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22619             
22620             output = output.replace(swapper, sw[1]);
22621         });
22622         
22623         return output;
22624     },
22625     
22626     
22627     cleanUpChildren : function (n)
22628     {
22629         if (!n.childNodes.length) {
22630             return;
22631         }
22632         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22633            this.cleanUpChild(n.childNodes[i]);
22634         }
22635     },
22636     
22637     
22638         
22639     
22640     cleanUpChild : function (node)
22641     {
22642         var ed = this;
22643         //console.log(node);
22644         if (node.nodeName == "#text") {
22645             // clean up silly Windows -- stuff?
22646             return; 
22647         }
22648         if (node.nodeName == "#comment") {
22649             node.parentNode.removeChild(node);
22650             // clean up silly Windows -- stuff?
22651             return; 
22652         }
22653         var lcname = node.tagName.toLowerCase();
22654         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22655         // whitelist of tags..
22656         
22657         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22658             // remove node.
22659             node.parentNode.removeChild(node);
22660             return;
22661             
22662         }
22663         
22664         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22665         
22666         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22667         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22668         
22669         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22670         //    remove_keep_children = true;
22671         //}
22672         
22673         if (remove_keep_children) {
22674             this.cleanUpChildren(node);
22675             // inserts everything just before this node...
22676             while (node.childNodes.length) {
22677                 var cn = node.childNodes[0];
22678                 node.removeChild(cn);
22679                 node.parentNode.insertBefore(cn, node);
22680             }
22681             node.parentNode.removeChild(node);
22682             return;
22683         }
22684         
22685         if (!node.attributes || !node.attributes.length) {
22686             this.cleanUpChildren(node);
22687             return;
22688         }
22689         
22690         function cleanAttr(n,v)
22691         {
22692             
22693             if (v.match(/^\./) || v.match(/^\//)) {
22694                 return;
22695             }
22696             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22697                 return;
22698             }
22699             if (v.match(/^#/)) {
22700                 return;
22701             }
22702 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22703             node.removeAttribute(n);
22704             
22705         }
22706         
22707         var cwhite = this.cwhite;
22708         var cblack = this.cblack;
22709             
22710         function cleanStyle(n,v)
22711         {
22712             if (v.match(/expression/)) { //XSS?? should we even bother..
22713                 node.removeAttribute(n);
22714                 return;
22715             }
22716             
22717             var parts = v.split(/;/);
22718             var clean = [];
22719             
22720             Roo.each(parts, function(p) {
22721                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22722                 if (!p.length) {
22723                     return true;
22724                 }
22725                 var l = p.split(':').shift().replace(/\s+/g,'');
22726                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22727                 
22728                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22729 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22730                     //node.removeAttribute(n);
22731                     return true;
22732                 }
22733                 //Roo.log()
22734                 // only allow 'c whitelisted system attributes'
22735                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22736 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22737                     //node.removeAttribute(n);
22738                     return true;
22739                 }
22740                 
22741                 
22742                  
22743                 
22744                 clean.push(p);
22745                 return true;
22746             });
22747             if (clean.length) { 
22748                 node.setAttribute(n, clean.join(';'));
22749             } else {
22750                 node.removeAttribute(n);
22751             }
22752             
22753         }
22754         
22755         
22756         for (var i = node.attributes.length-1; i > -1 ; i--) {
22757             var a = node.attributes[i];
22758             //console.log(a);
22759             
22760             if (a.name.toLowerCase().substr(0,2)=='on')  {
22761                 node.removeAttribute(a.name);
22762                 continue;
22763             }
22764             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22765                 node.removeAttribute(a.name);
22766                 continue;
22767             }
22768             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22769                 cleanAttr(a.name,a.value); // fixme..
22770                 continue;
22771             }
22772             if (a.name == 'style') {
22773                 cleanStyle(a.name,a.value);
22774                 continue;
22775             }
22776             /// clean up MS crap..
22777             // tecnically this should be a list of valid class'es..
22778             
22779             
22780             if (a.name == 'class') {
22781                 if (a.value.match(/^Mso/)) {
22782                     node.className = '';
22783                 }
22784                 
22785                 if (a.value.match(/^body$/)) {
22786                     node.className = '';
22787                 }
22788                 continue;
22789             }
22790             
22791             // style cleanup!?
22792             // class cleanup?
22793             
22794         }
22795         
22796         
22797         this.cleanUpChildren(node);
22798         
22799         
22800     },
22801     
22802     /**
22803      * Clean up MS wordisms...
22804      */
22805     cleanWord : function(node)
22806     {
22807         
22808         
22809         if (!node) {
22810             this.cleanWord(this.doc.body);
22811             return;
22812         }
22813         if (node.nodeName == "#text") {
22814             // clean up silly Windows -- stuff?
22815             return; 
22816         }
22817         if (node.nodeName == "#comment") {
22818             node.parentNode.removeChild(node);
22819             // clean up silly Windows -- stuff?
22820             return; 
22821         }
22822         
22823         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22824             node.parentNode.removeChild(node);
22825             return;
22826         }
22827         
22828         // remove - but keep children..
22829         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22830             while (node.childNodes.length) {
22831                 var cn = node.childNodes[0];
22832                 node.removeChild(cn);
22833                 node.parentNode.insertBefore(cn, node);
22834             }
22835             node.parentNode.removeChild(node);
22836             this.iterateChildren(node, this.cleanWord);
22837             return;
22838         }
22839         // clean styles
22840         if (node.className.length) {
22841             
22842             var cn = node.className.split(/\W+/);
22843             var cna = [];
22844             Roo.each(cn, function(cls) {
22845                 if (cls.match(/Mso[a-zA-Z]+/)) {
22846                     return;
22847                 }
22848                 cna.push(cls);
22849             });
22850             node.className = cna.length ? cna.join(' ') : '';
22851             if (!cna.length) {
22852                 node.removeAttribute("class");
22853             }
22854         }
22855         
22856         if (node.hasAttribute("lang")) {
22857             node.removeAttribute("lang");
22858         }
22859         
22860         if (node.hasAttribute("style")) {
22861             
22862             var styles = node.getAttribute("style").split(";");
22863             var nstyle = [];
22864             Roo.each(styles, function(s) {
22865                 if (!s.match(/:/)) {
22866                     return;
22867                 }
22868                 var kv = s.split(":");
22869                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22870                     return;
22871                 }
22872                 // what ever is left... we allow.
22873                 nstyle.push(s);
22874             });
22875             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22876             if (!nstyle.length) {
22877                 node.removeAttribute('style');
22878             }
22879         }
22880         this.iterateChildren(node, this.cleanWord);
22881         
22882         
22883         
22884     },
22885     /**
22886      * iterateChildren of a Node, calling fn each time, using this as the scole..
22887      * @param {DomNode} node node to iterate children of.
22888      * @param {Function} fn method of this class to call on each item.
22889      */
22890     iterateChildren : function(node, fn)
22891     {
22892         if (!node.childNodes.length) {
22893                 return;
22894         }
22895         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22896            fn.call(this, node.childNodes[i])
22897         }
22898     },
22899     
22900     
22901     /**
22902      * cleanTableWidths.
22903      *
22904      * Quite often pasting from word etc.. results in tables with column and widths.
22905      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22906      *
22907      */
22908     cleanTableWidths : function(node)
22909     {
22910          
22911          
22912         if (!node) {
22913             this.cleanTableWidths(this.doc.body);
22914             return;
22915         }
22916         
22917         // ignore list...
22918         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22919             return; 
22920         }
22921         Roo.log(node.tagName);
22922         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22923             this.iterateChildren(node, this.cleanTableWidths);
22924             return;
22925         }
22926         if (node.hasAttribute('width')) {
22927             node.removeAttribute('width');
22928         }
22929         
22930          
22931         if (node.hasAttribute("style")) {
22932             // pretty basic...
22933             
22934             var styles = node.getAttribute("style").split(";");
22935             var nstyle = [];
22936             Roo.each(styles, function(s) {
22937                 if (!s.match(/:/)) {
22938                     return;
22939                 }
22940                 var kv = s.split(":");
22941                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22942                     return;
22943                 }
22944                 // what ever is left... we allow.
22945                 nstyle.push(s);
22946             });
22947             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22948             if (!nstyle.length) {
22949                 node.removeAttribute('style');
22950             }
22951         }
22952         
22953         this.iterateChildren(node, this.cleanTableWidths);
22954         
22955         
22956     },
22957     
22958     
22959     
22960     
22961     domToHTML : function(currentElement, depth, nopadtext) {
22962         
22963         depth = depth || 0;
22964         nopadtext = nopadtext || false;
22965     
22966         if (!currentElement) {
22967             return this.domToHTML(this.doc.body);
22968         }
22969         
22970         //Roo.log(currentElement);
22971         var j;
22972         var allText = false;
22973         var nodeName = currentElement.nodeName;
22974         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22975         
22976         if  (nodeName == '#text') {
22977             
22978             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22979         }
22980         
22981         
22982         var ret = '';
22983         if (nodeName != 'BODY') {
22984              
22985             var i = 0;
22986             // Prints the node tagName, such as <A>, <IMG>, etc
22987             if (tagName) {
22988                 var attr = [];
22989                 for(i = 0; i < currentElement.attributes.length;i++) {
22990                     // quoting?
22991                     var aname = currentElement.attributes.item(i).name;
22992                     if (!currentElement.attributes.item(i).value.length) {
22993                         continue;
22994                     }
22995                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22996                 }
22997                 
22998                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22999             } 
23000             else {
23001                 
23002                 // eack
23003             }
23004         } else {
23005             tagName = false;
23006         }
23007         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23008             return ret;
23009         }
23010         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23011             nopadtext = true;
23012         }
23013         
23014         
23015         // Traverse the tree
23016         i = 0;
23017         var currentElementChild = currentElement.childNodes.item(i);
23018         var allText = true;
23019         var innerHTML  = '';
23020         lastnode = '';
23021         while (currentElementChild) {
23022             // Formatting code (indent the tree so it looks nice on the screen)
23023             var nopad = nopadtext;
23024             if (lastnode == 'SPAN') {
23025                 nopad  = true;
23026             }
23027             // text
23028             if  (currentElementChild.nodeName == '#text') {
23029                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23030                 toadd = nopadtext ? toadd : toadd.trim();
23031                 if (!nopad && toadd.length > 80) {
23032                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23033                 }
23034                 innerHTML  += toadd;
23035                 
23036                 i++;
23037                 currentElementChild = currentElement.childNodes.item(i);
23038                 lastNode = '';
23039                 continue;
23040             }
23041             allText = false;
23042             
23043             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23044                 
23045             // Recursively traverse the tree structure of the child node
23046             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23047             lastnode = currentElementChild.nodeName;
23048             i++;
23049             currentElementChild=currentElement.childNodes.item(i);
23050         }
23051         
23052         ret += innerHTML;
23053         
23054         if (!allText) {
23055                 // The remaining code is mostly for formatting the tree
23056             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23057         }
23058         
23059         
23060         if (tagName) {
23061             ret+= "</"+tagName+">";
23062         }
23063         return ret;
23064         
23065     },
23066         
23067     applyBlacklists : function()
23068     {
23069         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23070         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23071         
23072         this.white = [];
23073         this.black = [];
23074         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23075             if (b.indexOf(tag) > -1) {
23076                 return;
23077             }
23078             this.white.push(tag);
23079             
23080         }, this);
23081         
23082         Roo.each(w, function(tag) {
23083             if (b.indexOf(tag) > -1) {
23084                 return;
23085             }
23086             if (this.white.indexOf(tag) > -1) {
23087                 return;
23088             }
23089             this.white.push(tag);
23090             
23091         }, this);
23092         
23093         
23094         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23095             if (w.indexOf(tag) > -1) {
23096                 return;
23097             }
23098             this.black.push(tag);
23099             
23100         }, this);
23101         
23102         Roo.each(b, function(tag) {
23103             if (w.indexOf(tag) > -1) {
23104                 return;
23105             }
23106             if (this.black.indexOf(tag) > -1) {
23107                 return;
23108             }
23109             this.black.push(tag);
23110             
23111         }, this);
23112         
23113         
23114         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23115         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23116         
23117         this.cwhite = [];
23118         this.cblack = [];
23119         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23120             if (b.indexOf(tag) > -1) {
23121                 return;
23122             }
23123             this.cwhite.push(tag);
23124             
23125         }, this);
23126         
23127         Roo.each(w, function(tag) {
23128             if (b.indexOf(tag) > -1) {
23129                 return;
23130             }
23131             if (this.cwhite.indexOf(tag) > -1) {
23132                 return;
23133             }
23134             this.cwhite.push(tag);
23135             
23136         }, this);
23137         
23138         
23139         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23140             if (w.indexOf(tag) > -1) {
23141                 return;
23142             }
23143             this.cblack.push(tag);
23144             
23145         }, this);
23146         
23147         Roo.each(b, function(tag) {
23148             if (w.indexOf(tag) > -1) {
23149                 return;
23150             }
23151             if (this.cblack.indexOf(tag) > -1) {
23152                 return;
23153             }
23154             this.cblack.push(tag);
23155             
23156         }, this);
23157     },
23158     
23159     setStylesheets : function(stylesheets)
23160     {
23161         if(typeof(stylesheets) == 'string'){
23162             Roo.get(this.iframe.contentDocument.head).createChild({
23163                 tag : 'link',
23164                 rel : 'stylesheet',
23165                 type : 'text/css',
23166                 href : stylesheets
23167             });
23168             
23169             return;
23170         }
23171         var _this = this;
23172      
23173         Roo.each(stylesheets, function(s) {
23174             if(!s.length){
23175                 return;
23176             }
23177             
23178             Roo.get(_this.iframe.contentDocument.head).createChild({
23179                 tag : 'link',
23180                 rel : 'stylesheet',
23181                 type : 'text/css',
23182                 href : s
23183             });
23184         });
23185
23186         
23187     },
23188     
23189     removeStylesheets : function()
23190     {
23191         var _this = this;
23192         
23193         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23194             s.remove();
23195         });
23196     },
23197     
23198     setStyle : function(style)
23199     {
23200         Roo.get(this.iframe.contentDocument.head).createChild({
23201             tag : 'style',
23202             type : 'text/css',
23203             html : style
23204         });
23205
23206         return;
23207     }
23208     
23209     // hide stuff that is not compatible
23210     /**
23211      * @event blur
23212      * @hide
23213      */
23214     /**
23215      * @event change
23216      * @hide
23217      */
23218     /**
23219      * @event focus
23220      * @hide
23221      */
23222     /**
23223      * @event specialkey
23224      * @hide
23225      */
23226     /**
23227      * @cfg {String} fieldClass @hide
23228      */
23229     /**
23230      * @cfg {String} focusClass @hide
23231      */
23232     /**
23233      * @cfg {String} autoCreate @hide
23234      */
23235     /**
23236      * @cfg {String} inputType @hide
23237      */
23238     /**
23239      * @cfg {String} invalidClass @hide
23240      */
23241     /**
23242      * @cfg {String} invalidText @hide
23243      */
23244     /**
23245      * @cfg {String} msgFx @hide
23246      */
23247     /**
23248      * @cfg {String} validateOnBlur @hide
23249      */
23250 });
23251
23252 Roo.HtmlEditorCore.white = [
23253         'area', 'br', 'img', 'input', 'hr', 'wbr',
23254         
23255        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23256        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23257        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23258        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23259        'table',   'ul',         'xmp', 
23260        
23261        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23262       'thead',   'tr', 
23263      
23264       'dir', 'menu', 'ol', 'ul', 'dl',
23265        
23266       'embed',  'object'
23267 ];
23268
23269
23270 Roo.HtmlEditorCore.black = [
23271     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23272         'applet', // 
23273         'base',   'basefont', 'bgsound', 'blink',  'body', 
23274         'frame',  'frameset', 'head',    'html',   'ilayer', 
23275         'iframe', 'layer',  'link',     'meta',    'object',   
23276         'script', 'style' ,'title',  'xml' // clean later..
23277 ];
23278 Roo.HtmlEditorCore.clean = [
23279     'script', 'style', 'title', 'xml'
23280 ];
23281 Roo.HtmlEditorCore.remove = [
23282     'font'
23283 ];
23284 // attributes..
23285
23286 Roo.HtmlEditorCore.ablack = [
23287     'on'
23288 ];
23289     
23290 Roo.HtmlEditorCore.aclean = [ 
23291     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23292 ];
23293
23294 // protocols..
23295 Roo.HtmlEditorCore.pwhite= [
23296         'http',  'https',  'mailto'
23297 ];
23298
23299 // white listed style attributes.
23300 Roo.HtmlEditorCore.cwhite= [
23301       //  'text-align', /// default is to allow most things..
23302       
23303          
23304 //        'font-size'//??
23305 ];
23306
23307 // black listed style attributes.
23308 Roo.HtmlEditorCore.cblack= [
23309       //  'font-size' -- this can be set by the project 
23310 ];
23311
23312
23313 Roo.HtmlEditorCore.swapCodes   =[ 
23314     [    8211, "--" ], 
23315     [    8212, "--" ], 
23316     [    8216,  "'" ],  
23317     [    8217, "'" ],  
23318     [    8220, '"' ],  
23319     [    8221, '"' ],  
23320     [    8226, "*" ],  
23321     [    8230, "..." ]
23322 ]; 
23323
23324     /*
23325  * - LGPL
23326  *
23327  * HtmlEditor
23328  * 
23329  */
23330
23331 /**
23332  * @class Roo.bootstrap.HtmlEditor
23333  * @extends Roo.bootstrap.TextArea
23334  * Bootstrap HtmlEditor class
23335
23336  * @constructor
23337  * Create a new HtmlEditor
23338  * @param {Object} config The config object
23339  */
23340
23341 Roo.bootstrap.HtmlEditor = function(config){
23342     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23343     if (!this.toolbars) {
23344         this.toolbars = [];
23345     }
23346     
23347     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23348     this.addEvents({
23349             /**
23350              * @event initialize
23351              * Fires when the editor is fully initialized (including the iframe)
23352              * @param {HtmlEditor} this
23353              */
23354             initialize: true,
23355             /**
23356              * @event activate
23357              * Fires when the editor is first receives the focus. Any insertion must wait
23358              * until after this event.
23359              * @param {HtmlEditor} this
23360              */
23361             activate: true,
23362              /**
23363              * @event beforesync
23364              * Fires before the textarea is updated with content from the editor iframe. Return false
23365              * to cancel the sync.
23366              * @param {HtmlEditor} this
23367              * @param {String} html
23368              */
23369             beforesync: true,
23370              /**
23371              * @event beforepush
23372              * Fires before the iframe editor is updated with content from the textarea. Return false
23373              * to cancel the push.
23374              * @param {HtmlEditor} this
23375              * @param {String} html
23376              */
23377             beforepush: true,
23378              /**
23379              * @event sync
23380              * Fires when the textarea is updated with content from the editor iframe.
23381              * @param {HtmlEditor} this
23382              * @param {String} html
23383              */
23384             sync: true,
23385              /**
23386              * @event push
23387              * Fires when the iframe editor is updated with content from the textarea.
23388              * @param {HtmlEditor} this
23389              * @param {String} html
23390              */
23391             push: true,
23392              /**
23393              * @event editmodechange
23394              * Fires when the editor switches edit modes
23395              * @param {HtmlEditor} this
23396              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23397              */
23398             editmodechange: true,
23399             /**
23400              * @event editorevent
23401              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23402              * @param {HtmlEditor} this
23403              */
23404             editorevent: true,
23405             /**
23406              * @event firstfocus
23407              * Fires when on first focus - needed by toolbars..
23408              * @param {HtmlEditor} this
23409              */
23410             firstfocus: true,
23411             /**
23412              * @event autosave
23413              * Auto save the htmlEditor value as a file into Events
23414              * @param {HtmlEditor} this
23415              */
23416             autosave: true,
23417             /**
23418              * @event savedpreview
23419              * preview the saved version of htmlEditor
23420              * @param {HtmlEditor} this
23421              */
23422             savedpreview: true
23423         });
23424 };
23425
23426
23427 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23428     
23429     
23430       /**
23431      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23432      */
23433     toolbars : false,
23434     
23435      /**
23436     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23437     */
23438     btns : [],
23439    
23440      /**
23441      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23442      *                        Roo.resizable.
23443      */
23444     resizable : false,
23445      /**
23446      * @cfg {Number} height (in pixels)
23447      */   
23448     height: 300,
23449    /**
23450      * @cfg {Number} width (in pixels)
23451      */   
23452     width: false,
23453     
23454     /**
23455      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23456      * 
23457      */
23458     stylesheets: false,
23459     
23460     // id of frame..
23461     frameId: false,
23462     
23463     // private properties
23464     validationEvent : false,
23465     deferHeight: true,
23466     initialized : false,
23467     activated : false,
23468     
23469     onFocus : Roo.emptyFn,
23470     iframePad:3,
23471     hideMode:'offsets',
23472     
23473     tbContainer : false,
23474     
23475     bodyCls : '',
23476     
23477     toolbarContainer :function() {
23478         return this.wrap.select('.x-html-editor-tb',true).first();
23479     },
23480
23481     /**
23482      * Protected method that will not generally be called directly. It
23483      * is called when the editor creates its toolbar. Override this method if you need to
23484      * add custom toolbar buttons.
23485      * @param {HtmlEditor} editor
23486      */
23487     createToolbar : function(){
23488         Roo.log('renewing');
23489         Roo.log("create toolbars");
23490         
23491         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23492         this.toolbars[0].render(this.toolbarContainer());
23493         
23494         return;
23495         
23496 //        if (!editor.toolbars || !editor.toolbars.length) {
23497 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23498 //        }
23499 //        
23500 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23501 //            editor.toolbars[i] = Roo.factory(
23502 //                    typeof(editor.toolbars[i]) == 'string' ?
23503 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23504 //                Roo.bootstrap.HtmlEditor);
23505 //            editor.toolbars[i].init(editor);
23506 //        }
23507     },
23508
23509      
23510     // private
23511     onRender : function(ct, position)
23512     {
23513        // Roo.log("Call onRender: " + this.xtype);
23514         var _t = this;
23515         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23516       
23517         this.wrap = this.inputEl().wrap({
23518             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23519         });
23520         
23521         this.editorcore.onRender(ct, position);
23522          
23523         if (this.resizable) {
23524             this.resizeEl = new Roo.Resizable(this.wrap, {
23525                 pinned : true,
23526                 wrap: true,
23527                 dynamic : true,
23528                 minHeight : this.height,
23529                 height: this.height,
23530                 handles : this.resizable,
23531                 width: this.width,
23532                 listeners : {
23533                     resize : function(r, w, h) {
23534                         _t.onResize(w,h); // -something
23535                     }
23536                 }
23537             });
23538             
23539         }
23540         this.createToolbar(this);
23541        
23542         
23543         if(!this.width && this.resizable){
23544             this.setSize(this.wrap.getSize());
23545         }
23546         if (this.resizeEl) {
23547             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23548             // should trigger onReize..
23549         }
23550         
23551     },
23552
23553     // private
23554     onResize : function(w, h)
23555     {
23556         Roo.log('resize: ' +w + ',' + h );
23557         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23558         var ew = false;
23559         var eh = false;
23560         
23561         if(this.inputEl() ){
23562             if(typeof w == 'number'){
23563                 var aw = w - this.wrap.getFrameWidth('lr');
23564                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23565                 ew = aw;
23566             }
23567             if(typeof h == 'number'){
23568                  var tbh = -11;  // fixme it needs to tool bar size!
23569                 for (var i =0; i < this.toolbars.length;i++) {
23570                     // fixme - ask toolbars for heights?
23571                     tbh += this.toolbars[i].el.getHeight();
23572                     //if (this.toolbars[i].footer) {
23573                     //    tbh += this.toolbars[i].footer.el.getHeight();
23574                     //}
23575                 }
23576               
23577                 
23578                 
23579                 
23580                 
23581                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23582                 ah -= 5; // knock a few pixes off for look..
23583                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23584                 var eh = ah;
23585             }
23586         }
23587         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23588         this.editorcore.onResize(ew,eh);
23589         
23590     },
23591
23592     /**
23593      * Toggles the editor between standard and source edit mode.
23594      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23595      */
23596     toggleSourceEdit : function(sourceEditMode)
23597     {
23598         this.editorcore.toggleSourceEdit(sourceEditMode);
23599         
23600         if(this.editorcore.sourceEditMode){
23601             Roo.log('editor - showing textarea');
23602             
23603 //            Roo.log('in');
23604 //            Roo.log(this.syncValue());
23605             this.syncValue();
23606             this.inputEl().removeClass(['hide', 'x-hidden']);
23607             this.inputEl().dom.removeAttribute('tabIndex');
23608             this.inputEl().focus();
23609         }else{
23610             Roo.log('editor - hiding textarea');
23611 //            Roo.log('out')
23612 //            Roo.log(this.pushValue()); 
23613             this.pushValue();
23614             
23615             this.inputEl().addClass(['hide', 'x-hidden']);
23616             this.inputEl().dom.setAttribute('tabIndex', -1);
23617             //this.deferFocus();
23618         }
23619          
23620         if(this.resizable){
23621             this.setSize(this.wrap.getSize());
23622         }
23623         
23624         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23625     },
23626  
23627     // private (for BoxComponent)
23628     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23629
23630     // private (for BoxComponent)
23631     getResizeEl : function(){
23632         return this.wrap;
23633     },
23634
23635     // private (for BoxComponent)
23636     getPositionEl : function(){
23637         return this.wrap;
23638     },
23639
23640     // private
23641     initEvents : function(){
23642         this.originalValue = this.getValue();
23643     },
23644
23645 //    /**
23646 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23647 //     * @method
23648 //     */
23649 //    markInvalid : Roo.emptyFn,
23650 //    /**
23651 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23652 //     * @method
23653 //     */
23654 //    clearInvalid : Roo.emptyFn,
23655
23656     setValue : function(v){
23657         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23658         this.editorcore.pushValue();
23659     },
23660
23661      
23662     // private
23663     deferFocus : function(){
23664         this.focus.defer(10, this);
23665     },
23666
23667     // doc'ed in Field
23668     focus : function(){
23669         this.editorcore.focus();
23670         
23671     },
23672       
23673
23674     // private
23675     onDestroy : function(){
23676         
23677         
23678         
23679         if(this.rendered){
23680             
23681             for (var i =0; i < this.toolbars.length;i++) {
23682                 // fixme - ask toolbars for heights?
23683                 this.toolbars[i].onDestroy();
23684             }
23685             
23686             this.wrap.dom.innerHTML = '';
23687             this.wrap.remove();
23688         }
23689     },
23690
23691     // private
23692     onFirstFocus : function(){
23693         //Roo.log("onFirstFocus");
23694         this.editorcore.onFirstFocus();
23695          for (var i =0; i < this.toolbars.length;i++) {
23696             this.toolbars[i].onFirstFocus();
23697         }
23698         
23699     },
23700     
23701     // private
23702     syncValue : function()
23703     {   
23704         this.editorcore.syncValue();
23705     },
23706     
23707     pushValue : function()
23708     {   
23709         this.editorcore.pushValue();
23710     }
23711      
23712     
23713     // hide stuff that is not compatible
23714     /**
23715      * @event blur
23716      * @hide
23717      */
23718     /**
23719      * @event change
23720      * @hide
23721      */
23722     /**
23723      * @event focus
23724      * @hide
23725      */
23726     /**
23727      * @event specialkey
23728      * @hide
23729      */
23730     /**
23731      * @cfg {String} fieldClass @hide
23732      */
23733     /**
23734      * @cfg {String} focusClass @hide
23735      */
23736     /**
23737      * @cfg {String} autoCreate @hide
23738      */
23739     /**
23740      * @cfg {String} inputType @hide
23741      */
23742     /**
23743      * @cfg {String} invalidClass @hide
23744      */
23745     /**
23746      * @cfg {String} invalidText @hide
23747      */
23748     /**
23749      * @cfg {String} msgFx @hide
23750      */
23751     /**
23752      * @cfg {String} validateOnBlur @hide
23753      */
23754 });
23755  
23756     
23757    
23758    
23759    
23760       
23761 Roo.namespace('Roo.bootstrap.htmleditor');
23762 /**
23763  * @class Roo.bootstrap.HtmlEditorToolbar1
23764  * Basic Toolbar
23765  * 
23766  * Usage:
23767  *
23768  new Roo.bootstrap.HtmlEditor({
23769     ....
23770     toolbars : [
23771         new Roo.bootstrap.HtmlEditorToolbar1({
23772             disable : { fonts: 1 , format: 1, ..., ... , ...],
23773             btns : [ .... ]
23774         })
23775     }
23776      
23777  * 
23778  * @cfg {Object} disable List of elements to disable..
23779  * @cfg {Array} btns List of additional buttons.
23780  * 
23781  * 
23782  * NEEDS Extra CSS? 
23783  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23784  */
23785  
23786 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23787 {
23788     
23789     Roo.apply(this, config);
23790     
23791     // default disabled, based on 'good practice'..
23792     this.disable = this.disable || {};
23793     Roo.applyIf(this.disable, {
23794         fontSize : true,
23795         colors : true,
23796         specialElements : true
23797     });
23798     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23799     
23800     this.editor = config.editor;
23801     this.editorcore = config.editor.editorcore;
23802     
23803     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23804     
23805     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23806     // dont call parent... till later.
23807 }
23808 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23809      
23810     bar : true,
23811     
23812     editor : false,
23813     editorcore : false,
23814     
23815     
23816     formats : [
23817         "p" ,  
23818         "h1","h2","h3","h4","h5","h6", 
23819         "pre", "code", 
23820         "abbr", "acronym", "address", "cite", "samp", "var",
23821         'div','span'
23822     ],
23823     
23824     onRender : function(ct, position)
23825     {
23826        // Roo.log("Call onRender: " + this.xtype);
23827         
23828        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23829        Roo.log(this.el);
23830        this.el.dom.style.marginBottom = '0';
23831        var _this = this;
23832        var editorcore = this.editorcore;
23833        var editor= this.editor;
23834        
23835        var children = [];
23836        var btn = function(id,cmd , toggle, handler, html){
23837        
23838             var  event = toggle ? 'toggle' : 'click';
23839        
23840             var a = {
23841                 size : 'sm',
23842                 xtype: 'Button',
23843                 xns: Roo.bootstrap,
23844                 glyphicon : id,
23845                 cmd : id || cmd,
23846                 enableToggle:toggle !== false,
23847                 html : html || '',
23848                 pressed : toggle ? false : null,
23849                 listeners : {}
23850             };
23851             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23852                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23853             };
23854             children.push(a);
23855             return a;
23856        }
23857        
23858     //    var cb_box = function...
23859         
23860         var style = {
23861                 xtype: 'Button',
23862                 size : 'sm',
23863                 xns: Roo.bootstrap,
23864                 glyphicon : 'font',
23865                 //html : 'submit'
23866                 menu : {
23867                     xtype: 'Menu',
23868                     xns: Roo.bootstrap,
23869                     items:  []
23870                 }
23871         };
23872         Roo.each(this.formats, function(f) {
23873             style.menu.items.push({
23874                 xtype :'MenuItem',
23875                 xns: Roo.bootstrap,
23876                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23877                 tagname : f,
23878                 listeners : {
23879                     click : function()
23880                     {
23881                         editorcore.insertTag(this.tagname);
23882                         editor.focus();
23883                     }
23884                 }
23885                 
23886             });
23887         });
23888         children.push(style);   
23889         
23890         btn('bold',false,true);
23891         btn('italic',false,true);
23892         btn('align-left', 'justifyleft',true);
23893         btn('align-center', 'justifycenter',true);
23894         btn('align-right' , 'justifyright',true);
23895         btn('link', false, false, function(btn) {
23896             //Roo.log("create link?");
23897             var url = prompt(this.createLinkText, this.defaultLinkValue);
23898             if(url && url != 'http:/'+'/'){
23899                 this.editorcore.relayCmd('createlink', url);
23900             }
23901         }),
23902         btn('list','insertunorderedlist',true);
23903         btn('pencil', false,true, function(btn){
23904                 Roo.log(this);
23905                 this.toggleSourceEdit(btn.pressed);
23906         });
23907         
23908         if (this.editor.btns.length > 0) {
23909             for (var i = 0; i<this.editor.btns.length; i++) {
23910                 children.push(this.editor.btns[i]);
23911             }
23912         }
23913         
23914         /*
23915         var cog = {
23916                 xtype: 'Button',
23917                 size : 'sm',
23918                 xns: Roo.bootstrap,
23919                 glyphicon : 'cog',
23920                 //html : 'submit'
23921                 menu : {
23922                     xtype: 'Menu',
23923                     xns: Roo.bootstrap,
23924                     items:  []
23925                 }
23926         };
23927         
23928         cog.menu.items.push({
23929             xtype :'MenuItem',
23930             xns: Roo.bootstrap,
23931             html : Clean styles,
23932             tagname : f,
23933             listeners : {
23934                 click : function()
23935                 {
23936                     editorcore.insertTag(this.tagname);
23937                     editor.focus();
23938                 }
23939             }
23940             
23941         });
23942        */
23943         
23944          
23945        this.xtype = 'NavSimplebar';
23946         
23947         for(var i=0;i< children.length;i++) {
23948             
23949             this.buttons.add(this.addxtypeChild(children[i]));
23950             
23951         }
23952         
23953         editor.on('editorevent', this.updateToolbar, this);
23954     },
23955     onBtnClick : function(id)
23956     {
23957        this.editorcore.relayCmd(id);
23958        this.editorcore.focus();
23959     },
23960     
23961     /**
23962      * Protected method that will not generally be called directly. It triggers
23963      * a toolbar update by reading the markup state of the current selection in the editor.
23964      */
23965     updateToolbar: function(){
23966
23967         if(!this.editorcore.activated){
23968             this.editor.onFirstFocus(); // is this neeed?
23969             return;
23970         }
23971
23972         var btns = this.buttons; 
23973         var doc = this.editorcore.doc;
23974         btns.get('bold').setActive(doc.queryCommandState('bold'));
23975         btns.get('italic').setActive(doc.queryCommandState('italic'));
23976         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23977         
23978         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23979         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23980         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23981         
23982         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23983         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23984          /*
23985         
23986         var ans = this.editorcore.getAllAncestors();
23987         if (this.formatCombo) {
23988             
23989             
23990             var store = this.formatCombo.store;
23991             this.formatCombo.setValue("");
23992             for (var i =0; i < ans.length;i++) {
23993                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23994                     // select it..
23995                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23996                     break;
23997                 }
23998             }
23999         }
24000         
24001         
24002         
24003         // hides menus... - so this cant be on a menu...
24004         Roo.bootstrap.MenuMgr.hideAll();
24005         */
24006         Roo.bootstrap.MenuMgr.hideAll();
24007         //this.editorsyncValue();
24008     },
24009     onFirstFocus: function() {
24010         this.buttons.each(function(item){
24011            item.enable();
24012         });
24013     },
24014     toggleSourceEdit : function(sourceEditMode){
24015         
24016           
24017         if(sourceEditMode){
24018             Roo.log("disabling buttons");
24019            this.buttons.each( function(item){
24020                 if(item.cmd != 'pencil'){
24021                     item.disable();
24022                 }
24023             });
24024           
24025         }else{
24026             Roo.log("enabling buttons");
24027             if(this.editorcore.initialized){
24028                 this.buttons.each( function(item){
24029                     item.enable();
24030                 });
24031             }
24032             
24033         }
24034         Roo.log("calling toggole on editor");
24035         // tell the editor that it's been pressed..
24036         this.editor.toggleSourceEdit(sourceEditMode);
24037        
24038     }
24039 });
24040
24041
24042
24043
24044
24045 /**
24046  * @class Roo.bootstrap.Table.AbstractSelectionModel
24047  * @extends Roo.util.Observable
24048  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24049  * implemented by descendant classes.  This class should not be directly instantiated.
24050  * @constructor
24051  */
24052 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24053     this.locked = false;
24054     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24055 };
24056
24057
24058 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24059     /** @ignore Called by the grid automatically. Do not call directly. */
24060     init : function(grid){
24061         this.grid = grid;
24062         this.initEvents();
24063     },
24064
24065     /**
24066      * Locks the selections.
24067      */
24068     lock : function(){
24069         this.locked = true;
24070     },
24071
24072     /**
24073      * Unlocks the selections.
24074      */
24075     unlock : function(){
24076         this.locked = false;
24077     },
24078
24079     /**
24080      * Returns true if the selections are locked.
24081      * @return {Boolean}
24082      */
24083     isLocked : function(){
24084         return this.locked;
24085     }
24086 });
24087 /**
24088  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24089  * @class Roo.bootstrap.Table.RowSelectionModel
24090  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24091  * It supports multiple selections and keyboard selection/navigation. 
24092  * @constructor
24093  * @param {Object} config
24094  */
24095
24096 Roo.bootstrap.Table.RowSelectionModel = function(config){
24097     Roo.apply(this, config);
24098     this.selections = new Roo.util.MixedCollection(false, function(o){
24099         return o.id;
24100     });
24101
24102     this.last = false;
24103     this.lastActive = false;
24104
24105     this.addEvents({
24106         /**
24107              * @event selectionchange
24108              * Fires when the selection changes
24109              * @param {SelectionModel} this
24110              */
24111             "selectionchange" : true,
24112         /**
24113              * @event afterselectionchange
24114              * Fires after the selection changes (eg. by key press or clicking)
24115              * @param {SelectionModel} this
24116              */
24117             "afterselectionchange" : true,
24118         /**
24119              * @event beforerowselect
24120              * Fires when a row is selected being selected, return false to cancel.
24121              * @param {SelectionModel} this
24122              * @param {Number} rowIndex The selected index
24123              * @param {Boolean} keepExisting False if other selections will be cleared
24124              */
24125             "beforerowselect" : true,
24126         /**
24127              * @event rowselect
24128              * Fires when a row is selected.
24129              * @param {SelectionModel} this
24130              * @param {Number} rowIndex The selected index
24131              * @param {Roo.data.Record} r The record
24132              */
24133             "rowselect" : true,
24134         /**
24135              * @event rowdeselect
24136              * Fires when a row is deselected.
24137              * @param {SelectionModel} this
24138              * @param {Number} rowIndex The selected index
24139              */
24140         "rowdeselect" : true
24141     });
24142     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24143     this.locked = false;
24144  };
24145
24146 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24147     /**
24148      * @cfg {Boolean} singleSelect
24149      * True to allow selection of only one row at a time (defaults to false)
24150      */
24151     singleSelect : false,
24152
24153     // private
24154     initEvents : function()
24155     {
24156
24157         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24158         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24159         //}else{ // allow click to work like normal
24160          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24161         //}
24162         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24163         this.grid.on("rowclick", this.handleMouseDown, this);
24164         
24165         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24166             "up" : function(e){
24167                 if(!e.shiftKey){
24168                     this.selectPrevious(e.shiftKey);
24169                 }else if(this.last !== false && this.lastActive !== false){
24170                     var last = this.last;
24171                     this.selectRange(this.last,  this.lastActive-1);
24172                     this.grid.getView().focusRow(this.lastActive);
24173                     if(last !== false){
24174                         this.last = last;
24175                     }
24176                 }else{
24177                     this.selectFirstRow();
24178                 }
24179                 this.fireEvent("afterselectionchange", this);
24180             },
24181             "down" : function(e){
24182                 if(!e.shiftKey){
24183                     this.selectNext(e.shiftKey);
24184                 }else if(this.last !== false && this.lastActive !== false){
24185                     var last = this.last;
24186                     this.selectRange(this.last,  this.lastActive+1);
24187                     this.grid.getView().focusRow(this.lastActive);
24188                     if(last !== false){
24189                         this.last = last;
24190                     }
24191                 }else{
24192                     this.selectFirstRow();
24193                 }
24194                 this.fireEvent("afterselectionchange", this);
24195             },
24196             scope: this
24197         });
24198         this.grid.store.on('load', function(){
24199             this.selections.clear();
24200         },this);
24201         /*
24202         var view = this.grid.view;
24203         view.on("refresh", this.onRefresh, this);
24204         view.on("rowupdated", this.onRowUpdated, this);
24205         view.on("rowremoved", this.onRemove, this);
24206         */
24207     },
24208
24209     // private
24210     onRefresh : function()
24211     {
24212         var ds = this.grid.store, i, v = this.grid.view;
24213         var s = this.selections;
24214         s.each(function(r){
24215             if((i = ds.indexOfId(r.id)) != -1){
24216                 v.onRowSelect(i);
24217             }else{
24218                 s.remove(r);
24219             }
24220         });
24221     },
24222
24223     // private
24224     onRemove : function(v, index, r){
24225         this.selections.remove(r);
24226     },
24227
24228     // private
24229     onRowUpdated : function(v, index, r){
24230         if(this.isSelected(r)){
24231             v.onRowSelect(index);
24232         }
24233     },
24234
24235     /**
24236      * Select records.
24237      * @param {Array} records The records to select
24238      * @param {Boolean} keepExisting (optional) True to keep existing selections
24239      */
24240     selectRecords : function(records, keepExisting)
24241     {
24242         if(!keepExisting){
24243             this.clearSelections();
24244         }
24245             var ds = this.grid.store;
24246         for(var i = 0, len = records.length; i < len; i++){
24247             this.selectRow(ds.indexOf(records[i]), true);
24248         }
24249     },
24250
24251     /**
24252      * Gets the number of selected rows.
24253      * @return {Number}
24254      */
24255     getCount : function(){
24256         return this.selections.length;
24257     },
24258
24259     /**
24260      * Selects the first row in the grid.
24261      */
24262     selectFirstRow : function(){
24263         this.selectRow(0);
24264     },
24265
24266     /**
24267      * Select the last row.
24268      * @param {Boolean} keepExisting (optional) True to keep existing selections
24269      */
24270     selectLastRow : function(keepExisting){
24271         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24272         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24273     },
24274
24275     /**
24276      * Selects the row immediately following the last selected row.
24277      * @param {Boolean} keepExisting (optional) True to keep existing selections
24278      */
24279     selectNext : function(keepExisting)
24280     {
24281             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24282             this.selectRow(this.last+1, keepExisting);
24283             this.grid.getView().focusRow(this.last);
24284         }
24285     },
24286
24287     /**
24288      * Selects the row that precedes the last selected row.
24289      * @param {Boolean} keepExisting (optional) True to keep existing selections
24290      */
24291     selectPrevious : function(keepExisting){
24292         if(this.last){
24293             this.selectRow(this.last-1, keepExisting);
24294             this.grid.getView().focusRow(this.last);
24295         }
24296     },
24297
24298     /**
24299      * Returns the selected records
24300      * @return {Array} Array of selected records
24301      */
24302     getSelections : function(){
24303         return [].concat(this.selections.items);
24304     },
24305
24306     /**
24307      * Returns the first selected record.
24308      * @return {Record}
24309      */
24310     getSelected : function(){
24311         return this.selections.itemAt(0);
24312     },
24313
24314
24315     /**
24316      * Clears all selections.
24317      */
24318     clearSelections : function(fast)
24319     {
24320         if(this.locked) {
24321             return;
24322         }
24323         if(fast !== true){
24324                 var ds = this.grid.store;
24325             var s = this.selections;
24326             s.each(function(r){
24327                 this.deselectRow(ds.indexOfId(r.id));
24328             }, this);
24329             s.clear();
24330         }else{
24331             this.selections.clear();
24332         }
24333         this.last = false;
24334     },
24335
24336
24337     /**
24338      * Selects all rows.
24339      */
24340     selectAll : function(){
24341         if(this.locked) {
24342             return;
24343         }
24344         this.selections.clear();
24345         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24346             this.selectRow(i, true);
24347         }
24348     },
24349
24350     /**
24351      * Returns True if there is a selection.
24352      * @return {Boolean}
24353      */
24354     hasSelection : function(){
24355         return this.selections.length > 0;
24356     },
24357
24358     /**
24359      * Returns True if the specified row is selected.
24360      * @param {Number/Record} record The record or index of the record to check
24361      * @return {Boolean}
24362      */
24363     isSelected : function(index){
24364             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24365         return (r && this.selections.key(r.id) ? true : false);
24366     },
24367
24368     /**
24369      * Returns True if the specified record id is selected.
24370      * @param {String} id The id of record to check
24371      * @return {Boolean}
24372      */
24373     isIdSelected : function(id){
24374         return (this.selections.key(id) ? true : false);
24375     },
24376
24377
24378     // private
24379     handleMouseDBClick : function(e, t){
24380         
24381     },
24382     // private
24383     handleMouseDown : function(e, t)
24384     {
24385             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24386         if(this.isLocked() || rowIndex < 0 ){
24387             return;
24388         };
24389         if(e.shiftKey && this.last !== false){
24390             var last = this.last;
24391             this.selectRange(last, rowIndex, e.ctrlKey);
24392             this.last = last; // reset the last
24393             t.focus();
24394     
24395         }else{
24396             var isSelected = this.isSelected(rowIndex);
24397             //Roo.log("select row:" + rowIndex);
24398             if(isSelected){
24399                 this.deselectRow(rowIndex);
24400             } else {
24401                         this.selectRow(rowIndex, true);
24402             }
24403     
24404             /*
24405                 if(e.button !== 0 && isSelected){
24406                 alert('rowIndex 2: ' + rowIndex);
24407                     view.focusRow(rowIndex);
24408                 }else if(e.ctrlKey && isSelected){
24409                     this.deselectRow(rowIndex);
24410                 }else if(!isSelected){
24411                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24412                     view.focusRow(rowIndex);
24413                 }
24414             */
24415         }
24416         this.fireEvent("afterselectionchange", this);
24417     },
24418     // private
24419     handleDragableRowClick :  function(grid, rowIndex, e) 
24420     {
24421         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24422             this.selectRow(rowIndex, false);
24423             grid.view.focusRow(rowIndex);
24424              this.fireEvent("afterselectionchange", this);
24425         }
24426     },
24427     
24428     /**
24429      * Selects multiple rows.
24430      * @param {Array} rows Array of the indexes of the row to select
24431      * @param {Boolean} keepExisting (optional) True to keep existing selections
24432      */
24433     selectRows : function(rows, keepExisting){
24434         if(!keepExisting){
24435             this.clearSelections();
24436         }
24437         for(var i = 0, len = rows.length; i < len; i++){
24438             this.selectRow(rows[i], true);
24439         }
24440     },
24441
24442     /**
24443      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24444      * @param {Number} startRow The index of the first row in the range
24445      * @param {Number} endRow The index of the last row in the range
24446      * @param {Boolean} keepExisting (optional) True to retain existing selections
24447      */
24448     selectRange : function(startRow, endRow, keepExisting){
24449         if(this.locked) {
24450             return;
24451         }
24452         if(!keepExisting){
24453             this.clearSelections();
24454         }
24455         if(startRow <= endRow){
24456             for(var i = startRow; i <= endRow; i++){
24457                 this.selectRow(i, true);
24458             }
24459         }else{
24460             for(var i = startRow; i >= endRow; i--){
24461                 this.selectRow(i, true);
24462             }
24463         }
24464     },
24465
24466     /**
24467      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24468      * @param {Number} startRow The index of the first row in the range
24469      * @param {Number} endRow The index of the last row in the range
24470      */
24471     deselectRange : function(startRow, endRow, preventViewNotify){
24472         if(this.locked) {
24473             return;
24474         }
24475         for(var i = startRow; i <= endRow; i++){
24476             this.deselectRow(i, preventViewNotify);
24477         }
24478     },
24479
24480     /**
24481      * Selects a row.
24482      * @param {Number} row The index of the row to select
24483      * @param {Boolean} keepExisting (optional) True to keep existing selections
24484      */
24485     selectRow : function(index, keepExisting, preventViewNotify)
24486     {
24487             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24488             return;
24489         }
24490         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24491             if(!keepExisting || this.singleSelect){
24492                 this.clearSelections();
24493             }
24494             
24495             var r = this.grid.store.getAt(index);
24496             //console.log('selectRow - record id :' + r.id);
24497             
24498             this.selections.add(r);
24499             this.last = this.lastActive = index;
24500             if(!preventViewNotify){
24501                 var proxy = new Roo.Element(
24502                                 this.grid.getRowDom(index)
24503                 );
24504                 proxy.addClass('bg-info info');
24505             }
24506             this.fireEvent("rowselect", this, index, r);
24507             this.fireEvent("selectionchange", this);
24508         }
24509     },
24510
24511     /**
24512      * Deselects a row.
24513      * @param {Number} row The index of the row to deselect
24514      */
24515     deselectRow : function(index, preventViewNotify)
24516     {
24517         if(this.locked) {
24518             return;
24519         }
24520         if(this.last == index){
24521             this.last = false;
24522         }
24523         if(this.lastActive == index){
24524             this.lastActive = false;
24525         }
24526         
24527         var r = this.grid.store.getAt(index);
24528         if (!r) {
24529             return;
24530         }
24531         
24532         this.selections.remove(r);
24533         //.console.log('deselectRow - record id :' + r.id);
24534         if(!preventViewNotify){
24535         
24536             var proxy = new Roo.Element(
24537                 this.grid.getRowDom(index)
24538             );
24539             proxy.removeClass('bg-info info');
24540         }
24541         this.fireEvent("rowdeselect", this, index);
24542         this.fireEvent("selectionchange", this);
24543     },
24544
24545     // private
24546     restoreLast : function(){
24547         if(this._last){
24548             this.last = this._last;
24549         }
24550     },
24551
24552     // private
24553     acceptsNav : function(row, col, cm){
24554         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24555     },
24556
24557     // private
24558     onEditorKey : function(field, e){
24559         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24560         if(k == e.TAB){
24561             e.stopEvent();
24562             ed.completeEdit();
24563             if(e.shiftKey){
24564                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24565             }else{
24566                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24567             }
24568         }else if(k == e.ENTER && !e.ctrlKey){
24569             e.stopEvent();
24570             ed.completeEdit();
24571             if(e.shiftKey){
24572                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24573             }else{
24574                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24575             }
24576         }else if(k == e.ESC){
24577             ed.cancelEdit();
24578         }
24579         if(newCell){
24580             g.startEditing(newCell[0], newCell[1]);
24581         }
24582     }
24583 });
24584 /*
24585  * Based on:
24586  * Ext JS Library 1.1.1
24587  * Copyright(c) 2006-2007, Ext JS, LLC.
24588  *
24589  * Originally Released Under LGPL - original licence link has changed is not relivant.
24590  *
24591  * Fork - LGPL
24592  * <script type="text/javascript">
24593  */
24594  
24595 /**
24596  * @class Roo.bootstrap.PagingToolbar
24597  * @extends Roo.bootstrap.NavSimplebar
24598  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24599  * @constructor
24600  * Create a new PagingToolbar
24601  * @param {Object} config The config object
24602  * @param {Roo.data.Store} store
24603  */
24604 Roo.bootstrap.PagingToolbar = function(config)
24605 {
24606     // old args format still supported... - xtype is prefered..
24607         // created from xtype...
24608     
24609     this.ds = config.dataSource;
24610     
24611     if (config.store && !this.ds) {
24612         this.store= Roo.factory(config.store, Roo.data);
24613         this.ds = this.store;
24614         this.ds.xmodule = this.xmodule || false;
24615     }
24616     
24617     this.toolbarItems = [];
24618     if (config.items) {
24619         this.toolbarItems = config.items;
24620     }
24621     
24622     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24623     
24624     this.cursor = 0;
24625     
24626     if (this.ds) { 
24627         this.bind(this.ds);
24628     }
24629     
24630     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24631     
24632 };
24633
24634 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24635     /**
24636      * @cfg {Roo.data.Store} dataSource
24637      * The underlying data store providing the paged data
24638      */
24639     /**
24640      * @cfg {String/HTMLElement/Element} container
24641      * container The id or element that will contain the toolbar
24642      */
24643     /**
24644      * @cfg {Boolean} displayInfo
24645      * True to display the displayMsg (defaults to false)
24646      */
24647     /**
24648      * @cfg {Number} pageSize
24649      * The number of records to display per page (defaults to 20)
24650      */
24651     pageSize: 20,
24652     /**
24653      * @cfg {String} displayMsg
24654      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24655      */
24656     displayMsg : 'Displaying {0} - {1} of {2}',
24657     /**
24658      * @cfg {String} emptyMsg
24659      * The message to display when no records are found (defaults to "No data to display")
24660      */
24661     emptyMsg : 'No data to display',
24662     /**
24663      * Customizable piece of the default paging text (defaults to "Page")
24664      * @type String
24665      */
24666     beforePageText : "Page",
24667     /**
24668      * Customizable piece of the default paging text (defaults to "of %0")
24669      * @type String
24670      */
24671     afterPageText : "of {0}",
24672     /**
24673      * Customizable piece of the default paging text (defaults to "First Page")
24674      * @type String
24675      */
24676     firstText : "First Page",
24677     /**
24678      * Customizable piece of the default paging text (defaults to "Previous Page")
24679      * @type String
24680      */
24681     prevText : "Previous Page",
24682     /**
24683      * Customizable piece of the default paging text (defaults to "Next Page")
24684      * @type String
24685      */
24686     nextText : "Next Page",
24687     /**
24688      * Customizable piece of the default paging text (defaults to "Last Page")
24689      * @type String
24690      */
24691     lastText : "Last Page",
24692     /**
24693      * Customizable piece of the default paging text (defaults to "Refresh")
24694      * @type String
24695      */
24696     refreshText : "Refresh",
24697
24698     buttons : false,
24699     // private
24700     onRender : function(ct, position) 
24701     {
24702         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24703         this.navgroup.parentId = this.id;
24704         this.navgroup.onRender(this.el, null);
24705         // add the buttons to the navgroup
24706         
24707         if(this.displayInfo){
24708             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24709             this.displayEl = this.el.select('.x-paging-info', true).first();
24710 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24711 //            this.displayEl = navel.el.select('span',true).first();
24712         }
24713         
24714         var _this = this;
24715         
24716         if(this.buttons){
24717             Roo.each(_this.buttons, function(e){ // this might need to use render????
24718                Roo.factory(e).render(_this.el);
24719             });
24720         }
24721             
24722         Roo.each(_this.toolbarItems, function(e) {
24723             _this.navgroup.addItem(e);
24724         });
24725         
24726         
24727         this.first = this.navgroup.addItem({
24728             tooltip: this.firstText,
24729             cls: "prev",
24730             icon : 'fa fa-backward',
24731             disabled: true,
24732             preventDefault: true,
24733             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24734         });
24735         
24736         this.prev =  this.navgroup.addItem({
24737             tooltip: this.prevText,
24738             cls: "prev",
24739             icon : 'fa fa-step-backward',
24740             disabled: true,
24741             preventDefault: true,
24742             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24743         });
24744     //this.addSeparator();
24745         
24746         
24747         var field = this.navgroup.addItem( {
24748             tagtype : 'span',
24749             cls : 'x-paging-position',
24750             
24751             html : this.beforePageText  +
24752                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24753                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24754          } ); //?? escaped?
24755         
24756         this.field = field.el.select('input', true).first();
24757         this.field.on("keydown", this.onPagingKeydown, this);
24758         this.field.on("focus", function(){this.dom.select();});
24759     
24760     
24761         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24762         //this.field.setHeight(18);
24763         //this.addSeparator();
24764         this.next = this.navgroup.addItem({
24765             tooltip: this.nextText,
24766             cls: "next",
24767             html : ' <i class="fa fa-step-forward">',
24768             disabled: true,
24769             preventDefault: true,
24770             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24771         });
24772         this.last = this.navgroup.addItem({
24773             tooltip: this.lastText,
24774             icon : 'fa fa-forward',
24775             cls: "next",
24776             disabled: true,
24777             preventDefault: true,
24778             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24779         });
24780     //this.addSeparator();
24781         this.loading = this.navgroup.addItem({
24782             tooltip: this.refreshText,
24783             icon: 'fa fa-refresh',
24784             preventDefault: true,
24785             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24786         });
24787         
24788     },
24789
24790     // private
24791     updateInfo : function(){
24792         if(this.displayEl){
24793             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24794             var msg = count == 0 ?
24795                 this.emptyMsg :
24796                 String.format(
24797                     this.displayMsg,
24798                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24799                 );
24800             this.displayEl.update(msg);
24801         }
24802     },
24803
24804     // private
24805     onLoad : function(ds, r, o)
24806     {
24807         this.cursor = o.params.start ? o.params.start : 0;
24808         
24809         var d = this.getPageData(),
24810             ap = d.activePage,
24811             ps = d.pages;
24812         
24813         
24814         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24815         this.field.dom.value = ap;
24816         this.first.setDisabled(ap == 1);
24817         this.prev.setDisabled(ap == 1);
24818         this.next.setDisabled(ap == ps);
24819         this.last.setDisabled(ap == ps);
24820         this.loading.enable();
24821         this.updateInfo();
24822     },
24823
24824     // private
24825     getPageData : function(){
24826         var total = this.ds.getTotalCount();
24827         return {
24828             total : total,
24829             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24830             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24831         };
24832     },
24833
24834     // private
24835     onLoadError : function(){
24836         this.loading.enable();
24837     },
24838
24839     // private
24840     onPagingKeydown : function(e){
24841         var k = e.getKey();
24842         var d = this.getPageData();
24843         if(k == e.RETURN){
24844             var v = this.field.dom.value, pageNum;
24845             if(!v || isNaN(pageNum = parseInt(v, 10))){
24846                 this.field.dom.value = d.activePage;
24847                 return;
24848             }
24849             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24850             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24851             e.stopEvent();
24852         }
24853         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))
24854         {
24855           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24856           this.field.dom.value = pageNum;
24857           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24858           e.stopEvent();
24859         }
24860         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24861         {
24862           var v = this.field.dom.value, pageNum; 
24863           var increment = (e.shiftKey) ? 10 : 1;
24864           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24865                 increment *= -1;
24866           }
24867           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24868             this.field.dom.value = d.activePage;
24869             return;
24870           }
24871           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24872           {
24873             this.field.dom.value = parseInt(v, 10) + increment;
24874             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24875             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24876           }
24877           e.stopEvent();
24878         }
24879     },
24880
24881     // private
24882     beforeLoad : function(){
24883         if(this.loading){
24884             this.loading.disable();
24885         }
24886     },
24887
24888     // private
24889     onClick : function(which){
24890         
24891         var ds = this.ds;
24892         if (!ds) {
24893             return;
24894         }
24895         
24896         switch(which){
24897             case "first":
24898                 ds.load({params:{start: 0, limit: this.pageSize}});
24899             break;
24900             case "prev":
24901                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24902             break;
24903             case "next":
24904                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24905             break;
24906             case "last":
24907                 var total = ds.getTotalCount();
24908                 var extra = total % this.pageSize;
24909                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24910                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24911             break;
24912             case "refresh":
24913                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24914             break;
24915         }
24916     },
24917
24918     /**
24919      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24920      * @param {Roo.data.Store} store The data store to unbind
24921      */
24922     unbind : function(ds){
24923         ds.un("beforeload", this.beforeLoad, this);
24924         ds.un("load", this.onLoad, this);
24925         ds.un("loadexception", this.onLoadError, this);
24926         ds.un("remove", this.updateInfo, this);
24927         ds.un("add", this.updateInfo, this);
24928         this.ds = undefined;
24929     },
24930
24931     /**
24932      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24933      * @param {Roo.data.Store} store The data store to bind
24934      */
24935     bind : function(ds){
24936         ds.on("beforeload", this.beforeLoad, this);
24937         ds.on("load", this.onLoad, this);
24938         ds.on("loadexception", this.onLoadError, this);
24939         ds.on("remove", this.updateInfo, this);
24940         ds.on("add", this.updateInfo, this);
24941         this.ds = ds;
24942     }
24943 });/*
24944  * - LGPL
24945  *
24946  * element
24947  * 
24948  */
24949
24950 /**
24951  * @class Roo.bootstrap.MessageBar
24952  * @extends Roo.bootstrap.Component
24953  * Bootstrap MessageBar class
24954  * @cfg {String} html contents of the MessageBar
24955  * @cfg {String} weight (info | success | warning | danger) default info
24956  * @cfg {String} beforeClass insert the bar before the given class
24957  * @cfg {Boolean} closable (true | false) default false
24958  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24959  * 
24960  * @constructor
24961  * Create a new Element
24962  * @param {Object} config The config object
24963  */
24964
24965 Roo.bootstrap.MessageBar = function(config){
24966     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24967 };
24968
24969 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24970     
24971     html: '',
24972     weight: 'info',
24973     closable: false,
24974     fixed: false,
24975     beforeClass: 'bootstrap-sticky-wrap',
24976     
24977     getAutoCreate : function(){
24978         
24979         var cfg = {
24980             tag: 'div',
24981             cls: 'alert alert-dismissable alert-' + this.weight,
24982             cn: [
24983                 {
24984                     tag: 'span',
24985                     cls: 'message',
24986                     html: this.html || ''
24987                 }
24988             ]
24989         };
24990         
24991         if(this.fixed){
24992             cfg.cls += ' alert-messages-fixed';
24993         }
24994         
24995         if(this.closable){
24996             cfg.cn.push({
24997                 tag: 'button',
24998                 cls: 'close',
24999                 html: 'x'
25000             });
25001         }
25002         
25003         return cfg;
25004     },
25005     
25006     onRender : function(ct, position)
25007     {
25008         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25009         
25010         if(!this.el){
25011             var cfg = Roo.apply({},  this.getAutoCreate());
25012             cfg.id = Roo.id();
25013             
25014             if (this.cls) {
25015                 cfg.cls += ' ' + this.cls;
25016             }
25017             if (this.style) {
25018                 cfg.style = this.style;
25019             }
25020             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25021             
25022             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25023         }
25024         
25025         this.el.select('>button.close').on('click', this.hide, this);
25026         
25027     },
25028     
25029     show : function()
25030     {
25031         if (!this.rendered) {
25032             this.render();
25033         }
25034         
25035         this.el.show();
25036         
25037         this.fireEvent('show', this);
25038         
25039     },
25040     
25041     hide : function()
25042     {
25043         if (!this.rendered) {
25044             this.render();
25045         }
25046         
25047         this.el.hide();
25048         
25049         this.fireEvent('hide', this);
25050     },
25051     
25052     update : function()
25053     {
25054 //        var e = this.el.dom.firstChild;
25055 //        
25056 //        if(this.closable){
25057 //            e = e.nextSibling;
25058 //        }
25059 //        
25060 //        e.data = this.html || '';
25061
25062         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25063     }
25064    
25065 });
25066
25067  
25068
25069      /*
25070  * - LGPL
25071  *
25072  * Graph
25073  * 
25074  */
25075
25076
25077 /**
25078  * @class Roo.bootstrap.Graph
25079  * @extends Roo.bootstrap.Component
25080  * Bootstrap Graph class
25081 > Prameters
25082  -sm {number} sm 4
25083  -md {number} md 5
25084  @cfg {String} graphtype  bar | vbar | pie
25085  @cfg {number} g_x coodinator | centre x (pie)
25086  @cfg {number} g_y coodinator | centre y (pie)
25087  @cfg {number} g_r radius (pie)
25088  @cfg {number} g_height height of the chart (respected by all elements in the set)
25089  @cfg {number} g_width width of the chart (respected by all elements in the set)
25090  @cfg {Object} title The title of the chart
25091     
25092  -{Array}  values
25093  -opts (object) options for the chart 
25094      o {
25095      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25096      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25097      o vgutter (number)
25098      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.
25099      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25100      o to
25101      o stretch (boolean)
25102      o }
25103  -opts (object) options for the pie
25104      o{
25105      o cut
25106      o startAngle (number)
25107      o endAngle (number)
25108      } 
25109  *
25110  * @constructor
25111  * Create a new Input
25112  * @param {Object} config The config object
25113  */
25114
25115 Roo.bootstrap.Graph = function(config){
25116     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25117     
25118     this.addEvents({
25119         // img events
25120         /**
25121          * @event click
25122          * The img click event for the img.
25123          * @param {Roo.EventObject} e
25124          */
25125         "click" : true
25126     });
25127 };
25128
25129 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25130     
25131     sm: 4,
25132     md: 5,
25133     graphtype: 'bar',
25134     g_height: 250,
25135     g_width: 400,
25136     g_x: 50,
25137     g_y: 50,
25138     g_r: 30,
25139     opts:{
25140         //g_colors: this.colors,
25141         g_type: 'soft',
25142         g_gutter: '20%'
25143
25144     },
25145     title : false,
25146
25147     getAutoCreate : function(){
25148         
25149         var cfg = {
25150             tag: 'div',
25151             html : null
25152         };
25153         
25154         
25155         return  cfg;
25156     },
25157
25158     onRender : function(ct,position){
25159         
25160         
25161         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25162         
25163         if (typeof(Raphael) == 'undefined') {
25164             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25165             return;
25166         }
25167         
25168         this.raphael = Raphael(this.el.dom);
25169         
25170                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25171                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25172                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25173                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25174                 /*
25175                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25176                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25177                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25178                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25179                 
25180                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25181                 r.barchart(330, 10, 300, 220, data1);
25182                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25183                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25184                 */
25185                 
25186                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25187                 // r.barchart(30, 30, 560, 250,  xdata, {
25188                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25189                 //     axis : "0 0 1 1",
25190                 //     axisxlabels :  xdata
25191                 //     //yvalues : cols,
25192                    
25193                 // });
25194 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25195 //        
25196 //        this.load(null,xdata,{
25197 //                axis : "0 0 1 1",
25198 //                axisxlabels :  xdata
25199 //                });
25200
25201     },
25202
25203     load : function(graphtype,xdata,opts)
25204     {
25205         this.raphael.clear();
25206         if(!graphtype) {
25207             graphtype = this.graphtype;
25208         }
25209         if(!opts){
25210             opts = this.opts;
25211         }
25212         var r = this.raphael,
25213             fin = function () {
25214                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25215             },
25216             fout = function () {
25217                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25218             },
25219             pfin = function() {
25220                 this.sector.stop();
25221                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25222
25223                 if (this.label) {
25224                     this.label[0].stop();
25225                     this.label[0].attr({ r: 7.5 });
25226                     this.label[1].attr({ "font-weight": 800 });
25227                 }
25228             },
25229             pfout = function() {
25230                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25231
25232                 if (this.label) {
25233                     this.label[0].animate({ r: 5 }, 500, "bounce");
25234                     this.label[1].attr({ "font-weight": 400 });
25235                 }
25236             };
25237
25238         switch(graphtype){
25239             case 'bar':
25240                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25241                 break;
25242             case 'hbar':
25243                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25244                 break;
25245             case 'pie':
25246 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25247 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25248 //            
25249                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25250                 
25251                 break;
25252
25253         }
25254         
25255         if(this.title){
25256             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25257         }
25258         
25259     },
25260     
25261     setTitle: function(o)
25262     {
25263         this.title = o;
25264     },
25265     
25266     initEvents: function() {
25267         
25268         if(!this.href){
25269             this.el.on('click', this.onClick, this);
25270         }
25271     },
25272     
25273     onClick : function(e)
25274     {
25275         Roo.log('img onclick');
25276         this.fireEvent('click', this, e);
25277     }
25278    
25279 });
25280
25281  
25282 /*
25283  * - LGPL
25284  *
25285  * numberBox
25286  * 
25287  */
25288 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25289
25290 /**
25291  * @class Roo.bootstrap.dash.NumberBox
25292  * @extends Roo.bootstrap.Component
25293  * Bootstrap NumberBox class
25294  * @cfg {String} headline Box headline
25295  * @cfg {String} content Box content
25296  * @cfg {String} icon Box icon
25297  * @cfg {String} footer Footer text
25298  * @cfg {String} fhref Footer href
25299  * 
25300  * @constructor
25301  * Create a new NumberBox
25302  * @param {Object} config The config object
25303  */
25304
25305
25306 Roo.bootstrap.dash.NumberBox = function(config){
25307     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25308     
25309 };
25310
25311 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25312     
25313     headline : '',
25314     content : '',
25315     icon : '',
25316     footer : '',
25317     fhref : '',
25318     ficon : '',
25319     
25320     getAutoCreate : function(){
25321         
25322         var cfg = {
25323             tag : 'div',
25324             cls : 'small-box ',
25325             cn : [
25326                 {
25327                     tag : 'div',
25328                     cls : 'inner',
25329                     cn :[
25330                         {
25331                             tag : 'h3',
25332                             cls : 'roo-headline',
25333                             html : this.headline
25334                         },
25335                         {
25336                             tag : 'p',
25337                             cls : 'roo-content',
25338                             html : this.content
25339                         }
25340                     ]
25341                 }
25342             ]
25343         };
25344         
25345         if(this.icon){
25346             cfg.cn.push({
25347                 tag : 'div',
25348                 cls : 'icon',
25349                 cn :[
25350                     {
25351                         tag : 'i',
25352                         cls : 'ion ' + this.icon
25353                     }
25354                 ]
25355             });
25356         }
25357         
25358         if(this.footer){
25359             var footer = {
25360                 tag : 'a',
25361                 cls : 'small-box-footer',
25362                 href : this.fhref || '#',
25363                 html : this.footer
25364             };
25365             
25366             cfg.cn.push(footer);
25367             
25368         }
25369         
25370         return  cfg;
25371     },
25372
25373     onRender : function(ct,position){
25374         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25375
25376
25377        
25378                 
25379     },
25380
25381     setHeadline: function (value)
25382     {
25383         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25384     },
25385     
25386     setFooter: function (value, href)
25387     {
25388         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25389         
25390         if(href){
25391             this.el.select('a.small-box-footer',true).first().attr('href', href);
25392         }
25393         
25394     },
25395
25396     setContent: function (value)
25397     {
25398         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25399     },
25400
25401     initEvents: function() 
25402     {   
25403         
25404     }
25405     
25406 });
25407
25408  
25409 /*
25410  * - LGPL
25411  *
25412  * TabBox
25413  * 
25414  */
25415 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25416
25417 /**
25418  * @class Roo.bootstrap.dash.TabBox
25419  * @extends Roo.bootstrap.Component
25420  * Bootstrap TabBox class
25421  * @cfg {String} title Title of the TabBox
25422  * @cfg {String} icon Icon of the TabBox
25423  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25424  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25425  * 
25426  * @constructor
25427  * Create a new TabBox
25428  * @param {Object} config The config object
25429  */
25430
25431
25432 Roo.bootstrap.dash.TabBox = function(config){
25433     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25434     this.addEvents({
25435         // raw events
25436         /**
25437          * @event addpane
25438          * When a pane is added
25439          * @param {Roo.bootstrap.dash.TabPane} pane
25440          */
25441         "addpane" : true,
25442         /**
25443          * @event activatepane
25444          * When a pane is activated
25445          * @param {Roo.bootstrap.dash.TabPane} pane
25446          */
25447         "activatepane" : true
25448         
25449          
25450     });
25451     
25452     this.panes = [];
25453 };
25454
25455 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25456
25457     title : '',
25458     icon : false,
25459     showtabs : true,
25460     tabScrollable : false,
25461     
25462     getChildContainer : function()
25463     {
25464         return this.el.select('.tab-content', true).first();
25465     },
25466     
25467     getAutoCreate : function(){
25468         
25469         var header = {
25470             tag: 'li',
25471             cls: 'pull-left header',
25472             html: this.title,
25473             cn : []
25474         };
25475         
25476         if(this.icon){
25477             header.cn.push({
25478                 tag: 'i',
25479                 cls: 'fa ' + this.icon
25480             });
25481         }
25482         
25483         var h = {
25484             tag: 'ul',
25485             cls: 'nav nav-tabs pull-right',
25486             cn: [
25487                 header
25488             ]
25489         };
25490         
25491         if(this.tabScrollable){
25492             h = {
25493                 tag: 'div',
25494                 cls: 'tab-header',
25495                 cn: [
25496                     {
25497                         tag: 'ul',
25498                         cls: 'nav nav-tabs pull-right',
25499                         cn: [
25500                             header
25501                         ]
25502                     }
25503                 ]
25504             };
25505         }
25506         
25507         var cfg = {
25508             tag: 'div',
25509             cls: 'nav-tabs-custom',
25510             cn: [
25511                 h,
25512                 {
25513                     tag: 'div',
25514                     cls: 'tab-content no-padding',
25515                     cn: []
25516                 }
25517             ]
25518         };
25519
25520         return  cfg;
25521     },
25522     initEvents : function()
25523     {
25524         //Roo.log('add add pane handler');
25525         this.on('addpane', this.onAddPane, this);
25526     },
25527      /**
25528      * Updates the box title
25529      * @param {String} html to set the title to.
25530      */
25531     setTitle : function(value)
25532     {
25533         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25534     },
25535     onAddPane : function(pane)
25536     {
25537         this.panes.push(pane);
25538         //Roo.log('addpane');
25539         //Roo.log(pane);
25540         // tabs are rendere left to right..
25541         if(!this.showtabs){
25542             return;
25543         }
25544         
25545         var ctr = this.el.select('.nav-tabs', true).first();
25546          
25547          
25548         var existing = ctr.select('.nav-tab',true);
25549         var qty = existing.getCount();;
25550         
25551         
25552         var tab = ctr.createChild({
25553             tag : 'li',
25554             cls : 'nav-tab' + (qty ? '' : ' active'),
25555             cn : [
25556                 {
25557                     tag : 'a',
25558                     href:'#',
25559                     html : pane.title
25560                 }
25561             ]
25562         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25563         pane.tab = tab;
25564         
25565         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25566         if (!qty) {
25567             pane.el.addClass('active');
25568         }
25569         
25570                 
25571     },
25572     onTabClick : function(ev,un,ob,pane)
25573     {
25574         //Roo.log('tab - prev default');
25575         ev.preventDefault();
25576         
25577         
25578         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25579         pane.tab.addClass('active');
25580         //Roo.log(pane.title);
25581         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25582         // technically we should have a deactivate event.. but maybe add later.
25583         // and it should not de-activate the selected tab...
25584         this.fireEvent('activatepane', pane);
25585         pane.el.addClass('active');
25586         pane.fireEvent('activate');
25587         
25588         
25589     },
25590     
25591     getActivePane : function()
25592     {
25593         var r = false;
25594         Roo.each(this.panes, function(p) {
25595             if(p.el.hasClass('active')){
25596                 r = p;
25597                 return false;
25598             }
25599             
25600             return;
25601         });
25602         
25603         return r;
25604     }
25605     
25606     
25607 });
25608
25609  
25610 /*
25611  * - LGPL
25612  *
25613  * Tab pane
25614  * 
25615  */
25616 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25617 /**
25618  * @class Roo.bootstrap.TabPane
25619  * @extends Roo.bootstrap.Component
25620  * Bootstrap TabPane class
25621  * @cfg {Boolean} active (false | true) Default false
25622  * @cfg {String} title title of panel
25623
25624  * 
25625  * @constructor
25626  * Create a new TabPane
25627  * @param {Object} config The config object
25628  */
25629
25630 Roo.bootstrap.dash.TabPane = function(config){
25631     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25632     
25633     this.addEvents({
25634         // raw events
25635         /**
25636          * @event activate
25637          * When a pane is activated
25638          * @param {Roo.bootstrap.dash.TabPane} pane
25639          */
25640         "activate" : true
25641          
25642     });
25643 };
25644
25645 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25646     
25647     active : false,
25648     title : '',
25649     
25650     // the tabBox that this is attached to.
25651     tab : false,
25652      
25653     getAutoCreate : function() 
25654     {
25655         var cfg = {
25656             tag: 'div',
25657             cls: 'tab-pane'
25658         };
25659         
25660         if(this.active){
25661             cfg.cls += ' active';
25662         }
25663         
25664         return cfg;
25665     },
25666     initEvents  : function()
25667     {
25668         //Roo.log('trigger add pane handler');
25669         this.parent().fireEvent('addpane', this)
25670     },
25671     
25672      /**
25673      * Updates the tab title 
25674      * @param {String} html to set the title to.
25675      */
25676     setTitle: function(str)
25677     {
25678         if (!this.tab) {
25679             return;
25680         }
25681         this.title = str;
25682         this.tab.select('a', true).first().dom.innerHTML = str;
25683         
25684     }
25685     
25686     
25687     
25688 });
25689
25690  
25691
25692
25693  /*
25694  * - LGPL
25695  *
25696  * menu
25697  * 
25698  */
25699 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25700
25701 /**
25702  * @class Roo.bootstrap.menu.Menu
25703  * @extends Roo.bootstrap.Component
25704  * Bootstrap Menu class - container for Menu
25705  * @cfg {String} html Text of the menu
25706  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25707  * @cfg {String} icon Font awesome icon
25708  * @cfg {String} pos Menu align to (top | bottom) default bottom
25709  * 
25710  * 
25711  * @constructor
25712  * Create a new Menu
25713  * @param {Object} config The config object
25714  */
25715
25716
25717 Roo.bootstrap.menu.Menu = function(config){
25718     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25719     
25720     this.addEvents({
25721         /**
25722          * @event beforeshow
25723          * Fires before this menu is displayed
25724          * @param {Roo.bootstrap.menu.Menu} this
25725          */
25726         beforeshow : true,
25727         /**
25728          * @event beforehide
25729          * Fires before this menu is hidden
25730          * @param {Roo.bootstrap.menu.Menu} this
25731          */
25732         beforehide : true,
25733         /**
25734          * @event show
25735          * Fires after this menu is displayed
25736          * @param {Roo.bootstrap.menu.Menu} this
25737          */
25738         show : true,
25739         /**
25740          * @event hide
25741          * Fires after this menu is hidden
25742          * @param {Roo.bootstrap.menu.Menu} this
25743          */
25744         hide : true,
25745         /**
25746          * @event click
25747          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25748          * @param {Roo.bootstrap.menu.Menu} this
25749          * @param {Roo.EventObject} e
25750          */
25751         click : true
25752     });
25753     
25754 };
25755
25756 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25757     
25758     submenu : false,
25759     html : '',
25760     weight : 'default',
25761     icon : false,
25762     pos : 'bottom',
25763     
25764     
25765     getChildContainer : function() {
25766         if(this.isSubMenu){
25767             return this.el;
25768         }
25769         
25770         return this.el.select('ul.dropdown-menu', true).first();  
25771     },
25772     
25773     getAutoCreate : function()
25774     {
25775         var text = [
25776             {
25777                 tag : 'span',
25778                 cls : 'roo-menu-text',
25779                 html : this.html
25780             }
25781         ];
25782         
25783         if(this.icon){
25784             text.unshift({
25785                 tag : 'i',
25786                 cls : 'fa ' + this.icon
25787             })
25788         }
25789         
25790         
25791         var cfg = {
25792             tag : 'div',
25793             cls : 'btn-group',
25794             cn : [
25795                 {
25796                     tag : 'button',
25797                     cls : 'dropdown-button btn btn-' + this.weight,
25798                     cn : text
25799                 },
25800                 {
25801                     tag : 'button',
25802                     cls : 'dropdown-toggle btn btn-' + this.weight,
25803                     cn : [
25804                         {
25805                             tag : 'span',
25806                             cls : 'caret'
25807                         }
25808                     ]
25809                 },
25810                 {
25811                     tag : 'ul',
25812                     cls : 'dropdown-menu'
25813                 }
25814             ]
25815             
25816         };
25817         
25818         if(this.pos == 'top'){
25819             cfg.cls += ' dropup';
25820         }
25821         
25822         if(this.isSubMenu){
25823             cfg = {
25824                 tag : 'ul',
25825                 cls : 'dropdown-menu'
25826             }
25827         }
25828         
25829         return cfg;
25830     },
25831     
25832     onRender : function(ct, position)
25833     {
25834         this.isSubMenu = ct.hasClass('dropdown-submenu');
25835         
25836         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25837     },
25838     
25839     initEvents : function() 
25840     {
25841         if(this.isSubMenu){
25842             return;
25843         }
25844         
25845         this.hidden = true;
25846         
25847         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25848         this.triggerEl.on('click', this.onTriggerPress, this);
25849         
25850         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25851         this.buttonEl.on('click', this.onClick, this);
25852         
25853     },
25854     
25855     list : function()
25856     {
25857         if(this.isSubMenu){
25858             return this.el;
25859         }
25860         
25861         return this.el.select('ul.dropdown-menu', true).first();
25862     },
25863     
25864     onClick : function(e)
25865     {
25866         this.fireEvent("click", this, e);
25867     },
25868     
25869     onTriggerPress  : function(e)
25870     {   
25871         if (this.isVisible()) {
25872             this.hide();
25873         } else {
25874             this.show();
25875         }
25876     },
25877     
25878     isVisible : function(){
25879         return !this.hidden;
25880     },
25881     
25882     show : function()
25883     {
25884         this.fireEvent("beforeshow", this);
25885         
25886         this.hidden = false;
25887         this.el.addClass('open');
25888         
25889         Roo.get(document).on("mouseup", this.onMouseUp, this);
25890         
25891         this.fireEvent("show", this);
25892         
25893         
25894     },
25895     
25896     hide : function()
25897     {
25898         this.fireEvent("beforehide", this);
25899         
25900         this.hidden = true;
25901         this.el.removeClass('open');
25902         
25903         Roo.get(document).un("mouseup", this.onMouseUp);
25904         
25905         this.fireEvent("hide", this);
25906     },
25907     
25908     onMouseUp : function()
25909     {
25910         this.hide();
25911     }
25912     
25913 });
25914
25915  
25916  /*
25917  * - LGPL
25918  *
25919  * menu item
25920  * 
25921  */
25922 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25923
25924 /**
25925  * @class Roo.bootstrap.menu.Item
25926  * @extends Roo.bootstrap.Component
25927  * Bootstrap MenuItem class
25928  * @cfg {Boolean} submenu (true | false) default false
25929  * @cfg {String} html text of the item
25930  * @cfg {String} href the link
25931  * @cfg {Boolean} disable (true | false) default false
25932  * @cfg {Boolean} preventDefault (true | false) default true
25933  * @cfg {String} icon Font awesome icon
25934  * @cfg {String} pos Submenu align to (left | right) default right 
25935  * 
25936  * 
25937  * @constructor
25938  * Create a new Item
25939  * @param {Object} config The config object
25940  */
25941
25942
25943 Roo.bootstrap.menu.Item = function(config){
25944     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25945     this.addEvents({
25946         /**
25947          * @event mouseover
25948          * Fires when the mouse is hovering over this menu
25949          * @param {Roo.bootstrap.menu.Item} this
25950          * @param {Roo.EventObject} e
25951          */
25952         mouseover : true,
25953         /**
25954          * @event mouseout
25955          * Fires when the mouse exits this menu
25956          * @param {Roo.bootstrap.menu.Item} this
25957          * @param {Roo.EventObject} e
25958          */
25959         mouseout : true,
25960         // raw events
25961         /**
25962          * @event click
25963          * The raw click event for the entire grid.
25964          * @param {Roo.EventObject} e
25965          */
25966         click : true
25967     });
25968 };
25969
25970 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25971     
25972     submenu : false,
25973     href : '',
25974     html : '',
25975     preventDefault: true,
25976     disable : false,
25977     icon : false,
25978     pos : 'right',
25979     
25980     getAutoCreate : function()
25981     {
25982         var text = [
25983             {
25984                 tag : 'span',
25985                 cls : 'roo-menu-item-text',
25986                 html : this.html
25987             }
25988         ];
25989         
25990         if(this.icon){
25991             text.unshift({
25992                 tag : 'i',
25993                 cls : 'fa ' + this.icon
25994             })
25995         }
25996         
25997         var cfg = {
25998             tag : 'li',
25999             cn : [
26000                 {
26001                     tag : 'a',
26002                     href : this.href || '#',
26003                     cn : text
26004                 }
26005             ]
26006         };
26007         
26008         if(this.disable){
26009             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26010         }
26011         
26012         if(this.submenu){
26013             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26014             
26015             if(this.pos == 'left'){
26016                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26017             }
26018         }
26019         
26020         return cfg;
26021     },
26022     
26023     initEvents : function() 
26024     {
26025         this.el.on('mouseover', this.onMouseOver, this);
26026         this.el.on('mouseout', this.onMouseOut, this);
26027         
26028         this.el.select('a', true).first().on('click', this.onClick, this);
26029         
26030     },
26031     
26032     onClick : function(e)
26033     {
26034         if(this.preventDefault){
26035             e.preventDefault();
26036         }
26037         
26038         this.fireEvent("click", this, e);
26039     },
26040     
26041     onMouseOver : function(e)
26042     {
26043         if(this.submenu && this.pos == 'left'){
26044             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26045         }
26046         
26047         this.fireEvent("mouseover", this, e);
26048     },
26049     
26050     onMouseOut : function(e)
26051     {
26052         this.fireEvent("mouseout", this, e);
26053     }
26054 });
26055
26056  
26057
26058  /*
26059  * - LGPL
26060  *
26061  * menu separator
26062  * 
26063  */
26064 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26065
26066 /**
26067  * @class Roo.bootstrap.menu.Separator
26068  * @extends Roo.bootstrap.Component
26069  * Bootstrap Separator class
26070  * 
26071  * @constructor
26072  * Create a new Separator
26073  * @param {Object} config The config object
26074  */
26075
26076
26077 Roo.bootstrap.menu.Separator = function(config){
26078     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26079 };
26080
26081 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26082     
26083     getAutoCreate : function(){
26084         var cfg = {
26085             tag : 'li',
26086             cls: 'divider'
26087         };
26088         
26089         return cfg;
26090     }
26091    
26092 });
26093
26094  
26095
26096  /*
26097  * - LGPL
26098  *
26099  * Tooltip
26100  * 
26101  */
26102
26103 /**
26104  * @class Roo.bootstrap.Tooltip
26105  * Bootstrap Tooltip class
26106  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26107  * to determine which dom element triggers the tooltip.
26108  * 
26109  * It needs to add support for additional attributes like tooltip-position
26110  * 
26111  * @constructor
26112  * Create a new Toolti
26113  * @param {Object} config The config object
26114  */
26115
26116 Roo.bootstrap.Tooltip = function(config){
26117     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26118     
26119     this.alignment = Roo.bootstrap.Tooltip.alignment;
26120     
26121     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26122         this.alignment = config.alignment;
26123     }
26124     
26125 };
26126
26127 Roo.apply(Roo.bootstrap.Tooltip, {
26128     /**
26129      * @function init initialize tooltip monitoring.
26130      * @static
26131      */
26132     currentEl : false,
26133     currentTip : false,
26134     currentRegion : false,
26135     
26136     //  init : delay?
26137     
26138     init : function()
26139     {
26140         Roo.get(document).on('mouseover', this.enter ,this);
26141         Roo.get(document).on('mouseout', this.leave, this);
26142          
26143         
26144         this.currentTip = new Roo.bootstrap.Tooltip();
26145     },
26146     
26147     enter : function(ev)
26148     {
26149         var dom = ev.getTarget();
26150         
26151         //Roo.log(['enter',dom]);
26152         var el = Roo.fly(dom);
26153         if (this.currentEl) {
26154             //Roo.log(dom);
26155             //Roo.log(this.currentEl);
26156             //Roo.log(this.currentEl.contains(dom));
26157             if (this.currentEl == el) {
26158                 return;
26159             }
26160             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26161                 return;
26162             }
26163
26164         }
26165         
26166         if (this.currentTip.el) {
26167             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26168         }    
26169         //Roo.log(ev);
26170         
26171         if(!el || el.dom == document){
26172             return;
26173         }
26174         
26175         var bindEl = el;
26176         
26177         // you can not look for children, as if el is the body.. then everythign is the child..
26178         if (!el.attr('tooltip')) { //
26179             if (!el.select("[tooltip]").elements.length) {
26180                 return;
26181             }
26182             // is the mouse over this child...?
26183             bindEl = el.select("[tooltip]").first();
26184             var xy = ev.getXY();
26185             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26186                 //Roo.log("not in region.");
26187                 return;
26188             }
26189             //Roo.log("child element over..");
26190             
26191         }
26192         this.currentEl = bindEl;
26193         this.currentTip.bind(bindEl);
26194         this.currentRegion = Roo.lib.Region.getRegion(dom);
26195         this.currentTip.enter();
26196         
26197     },
26198     leave : function(ev)
26199     {
26200         var dom = ev.getTarget();
26201         //Roo.log(['leave',dom]);
26202         if (!this.currentEl) {
26203             return;
26204         }
26205         
26206         
26207         if (dom != this.currentEl.dom) {
26208             return;
26209         }
26210         var xy = ev.getXY();
26211         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26212             return;
26213         }
26214         // only activate leave if mouse cursor is outside... bounding box..
26215         
26216         
26217         
26218         
26219         if (this.currentTip) {
26220             this.currentTip.leave();
26221         }
26222         //Roo.log('clear currentEl');
26223         this.currentEl = false;
26224         
26225         
26226     },
26227     alignment : {
26228         'left' : ['r-l', [-2,0], 'right'],
26229         'right' : ['l-r', [2,0], 'left'],
26230         'bottom' : ['t-b', [0,2], 'top'],
26231         'top' : [ 'b-t', [0,-2], 'bottom']
26232     }
26233     
26234 });
26235
26236
26237 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26238     
26239     
26240     bindEl : false,
26241     
26242     delay : null, // can be { show : 300 , hide: 500}
26243     
26244     timeout : null,
26245     
26246     hoverState : null, //???
26247     
26248     placement : 'bottom', 
26249     
26250     alignment : false,
26251     
26252     getAutoCreate : function(){
26253     
26254         var cfg = {
26255            cls : 'tooltip',
26256            role : 'tooltip',
26257            cn : [
26258                 {
26259                     cls : 'tooltip-arrow'
26260                 },
26261                 {
26262                     cls : 'tooltip-inner'
26263                 }
26264            ]
26265         };
26266         
26267         return cfg;
26268     },
26269     bind : function(el)
26270     {
26271         this.bindEl = el;
26272     },
26273       
26274     
26275     enter : function () {
26276        
26277         if (this.timeout != null) {
26278             clearTimeout(this.timeout);
26279         }
26280         
26281         this.hoverState = 'in';
26282          //Roo.log("enter - show");
26283         if (!this.delay || !this.delay.show) {
26284             this.show();
26285             return;
26286         }
26287         var _t = this;
26288         this.timeout = setTimeout(function () {
26289             if (_t.hoverState == 'in') {
26290                 _t.show();
26291             }
26292         }, this.delay.show);
26293     },
26294     leave : function()
26295     {
26296         clearTimeout(this.timeout);
26297     
26298         this.hoverState = 'out';
26299          if (!this.delay || !this.delay.hide) {
26300             this.hide();
26301             return;
26302         }
26303        
26304         var _t = this;
26305         this.timeout = setTimeout(function () {
26306             //Roo.log("leave - timeout");
26307             
26308             if (_t.hoverState == 'out') {
26309                 _t.hide();
26310                 Roo.bootstrap.Tooltip.currentEl = false;
26311             }
26312         }, delay);
26313     },
26314     
26315     show : function (msg)
26316     {
26317         if (!this.el) {
26318             this.render(document.body);
26319         }
26320         // set content.
26321         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26322         
26323         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26324         
26325         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26326         
26327         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26328         
26329         var placement = typeof this.placement == 'function' ?
26330             this.placement.call(this, this.el, on_el) :
26331             this.placement;
26332             
26333         var autoToken = /\s?auto?\s?/i;
26334         var autoPlace = autoToken.test(placement);
26335         if (autoPlace) {
26336             placement = placement.replace(autoToken, '') || 'top';
26337         }
26338         
26339         //this.el.detach()
26340         //this.el.setXY([0,0]);
26341         this.el.show();
26342         //this.el.dom.style.display='block';
26343         
26344         //this.el.appendTo(on_el);
26345         
26346         var p = this.getPosition();
26347         var box = this.el.getBox();
26348         
26349         if (autoPlace) {
26350             // fixme..
26351         }
26352         
26353         var align = this.alignment[placement];
26354         
26355         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26356         
26357         if(placement == 'top' || placement == 'bottom'){
26358             if(xy[0] < 0){
26359                 placement = 'right';
26360             }
26361             
26362             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26363                 placement = 'left';
26364             }
26365             
26366             var scroll = Roo.select('body', true).first().getScroll();
26367             
26368             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26369                 placement = 'top';
26370             }
26371             
26372             align = this.alignment[placement];
26373         }
26374         
26375         this.el.alignTo(this.bindEl, align[0],align[1]);
26376         //var arrow = this.el.select('.arrow',true).first();
26377         //arrow.set(align[2], 
26378         
26379         this.el.addClass(placement);
26380         
26381         this.el.addClass('in fade');
26382         
26383         this.hoverState = null;
26384         
26385         if (this.el.hasClass('fade')) {
26386             // fade it?
26387         }
26388         
26389     },
26390     hide : function()
26391     {
26392          
26393         if (!this.el) {
26394             return;
26395         }
26396         //this.el.setXY([0,0]);
26397         this.el.removeClass('in');
26398         //this.el.hide();
26399         
26400     }
26401     
26402 });
26403  
26404
26405  /*
26406  * - LGPL
26407  *
26408  * Location Picker
26409  * 
26410  */
26411
26412 /**
26413  * @class Roo.bootstrap.LocationPicker
26414  * @extends Roo.bootstrap.Component
26415  * Bootstrap LocationPicker class
26416  * @cfg {Number} latitude Position when init default 0
26417  * @cfg {Number} longitude Position when init default 0
26418  * @cfg {Number} zoom default 15
26419  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26420  * @cfg {Boolean} mapTypeControl default false
26421  * @cfg {Boolean} disableDoubleClickZoom default false
26422  * @cfg {Boolean} scrollwheel default true
26423  * @cfg {Boolean} streetViewControl default false
26424  * @cfg {Number} radius default 0
26425  * @cfg {String} locationName
26426  * @cfg {Boolean} draggable default true
26427  * @cfg {Boolean} enableAutocomplete default false
26428  * @cfg {Boolean} enableReverseGeocode default true
26429  * @cfg {String} markerTitle
26430  * 
26431  * @constructor
26432  * Create a new LocationPicker
26433  * @param {Object} config The config object
26434  */
26435
26436
26437 Roo.bootstrap.LocationPicker = function(config){
26438     
26439     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26440     
26441     this.addEvents({
26442         /**
26443          * @event initial
26444          * Fires when the picker initialized.
26445          * @param {Roo.bootstrap.LocationPicker} this
26446          * @param {Google Location} location
26447          */
26448         initial : true,
26449         /**
26450          * @event positionchanged
26451          * Fires when the picker position changed.
26452          * @param {Roo.bootstrap.LocationPicker} this
26453          * @param {Google Location} location
26454          */
26455         positionchanged : true,
26456         /**
26457          * @event resize
26458          * Fires when the map resize.
26459          * @param {Roo.bootstrap.LocationPicker} this
26460          */
26461         resize : true,
26462         /**
26463          * @event show
26464          * Fires when the map show.
26465          * @param {Roo.bootstrap.LocationPicker} this
26466          */
26467         show : true,
26468         /**
26469          * @event hide
26470          * Fires when the map hide.
26471          * @param {Roo.bootstrap.LocationPicker} this
26472          */
26473         hide : true,
26474         /**
26475          * @event mapClick
26476          * Fires when click the map.
26477          * @param {Roo.bootstrap.LocationPicker} this
26478          * @param {Map event} e
26479          */
26480         mapClick : true,
26481         /**
26482          * @event mapRightClick
26483          * Fires when right click the map.
26484          * @param {Roo.bootstrap.LocationPicker} this
26485          * @param {Map event} e
26486          */
26487         mapRightClick : true,
26488         /**
26489          * @event markerClick
26490          * Fires when click the marker.
26491          * @param {Roo.bootstrap.LocationPicker} this
26492          * @param {Map event} e
26493          */
26494         markerClick : true,
26495         /**
26496          * @event markerRightClick
26497          * Fires when right click the marker.
26498          * @param {Roo.bootstrap.LocationPicker} this
26499          * @param {Map event} e
26500          */
26501         markerRightClick : true,
26502         /**
26503          * @event OverlayViewDraw
26504          * Fires when OverlayView Draw
26505          * @param {Roo.bootstrap.LocationPicker} this
26506          */
26507         OverlayViewDraw : true,
26508         /**
26509          * @event OverlayViewOnAdd
26510          * Fires when OverlayView Draw
26511          * @param {Roo.bootstrap.LocationPicker} this
26512          */
26513         OverlayViewOnAdd : true,
26514         /**
26515          * @event OverlayViewOnRemove
26516          * Fires when OverlayView Draw
26517          * @param {Roo.bootstrap.LocationPicker} this
26518          */
26519         OverlayViewOnRemove : true,
26520         /**
26521          * @event OverlayViewShow
26522          * Fires when OverlayView Draw
26523          * @param {Roo.bootstrap.LocationPicker} this
26524          * @param {Pixel} cpx
26525          */
26526         OverlayViewShow : true,
26527         /**
26528          * @event OverlayViewHide
26529          * Fires when OverlayView Draw
26530          * @param {Roo.bootstrap.LocationPicker} this
26531          */
26532         OverlayViewHide : true,
26533         /**
26534          * @event loadexception
26535          * Fires when load google lib failed.
26536          * @param {Roo.bootstrap.LocationPicker} this
26537          */
26538         loadexception : true
26539     });
26540         
26541 };
26542
26543 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26544     
26545     gMapContext: false,
26546     
26547     latitude: 0,
26548     longitude: 0,
26549     zoom: 15,
26550     mapTypeId: false,
26551     mapTypeControl: false,
26552     disableDoubleClickZoom: false,
26553     scrollwheel: true,
26554     streetViewControl: false,
26555     radius: 0,
26556     locationName: '',
26557     draggable: true,
26558     enableAutocomplete: false,
26559     enableReverseGeocode: true,
26560     markerTitle: '',
26561     
26562     getAutoCreate: function()
26563     {
26564
26565         var cfg = {
26566             tag: 'div',
26567             cls: 'roo-location-picker'
26568         };
26569         
26570         return cfg
26571     },
26572     
26573     initEvents: function(ct, position)
26574     {       
26575         if(!this.el.getWidth() || this.isApplied()){
26576             return;
26577         }
26578         
26579         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26580         
26581         this.initial();
26582     },
26583     
26584     initial: function()
26585     {
26586         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26587             this.fireEvent('loadexception', this);
26588             return;
26589         }
26590         
26591         if(!this.mapTypeId){
26592             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26593         }
26594         
26595         this.gMapContext = this.GMapContext();
26596         
26597         this.initOverlayView();
26598         
26599         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26600         
26601         var _this = this;
26602                 
26603         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26604             _this.setPosition(_this.gMapContext.marker.position);
26605         });
26606         
26607         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26608             _this.fireEvent('mapClick', this, event);
26609             
26610         });
26611
26612         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26613             _this.fireEvent('mapRightClick', this, event);
26614             
26615         });
26616         
26617         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26618             _this.fireEvent('markerClick', this, event);
26619             
26620         });
26621
26622         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26623             _this.fireEvent('markerRightClick', this, event);
26624             
26625         });
26626         
26627         this.setPosition(this.gMapContext.location);
26628         
26629         this.fireEvent('initial', this, this.gMapContext.location);
26630     },
26631     
26632     initOverlayView: function()
26633     {
26634         var _this = this;
26635         
26636         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26637             
26638             draw: function()
26639             {
26640                 _this.fireEvent('OverlayViewDraw', _this);
26641             },
26642             
26643             onAdd: function()
26644             {
26645                 _this.fireEvent('OverlayViewOnAdd', _this);
26646             },
26647             
26648             onRemove: function()
26649             {
26650                 _this.fireEvent('OverlayViewOnRemove', _this);
26651             },
26652             
26653             show: function(cpx)
26654             {
26655                 _this.fireEvent('OverlayViewShow', _this, cpx);
26656             },
26657             
26658             hide: function()
26659             {
26660                 _this.fireEvent('OverlayViewHide', _this);
26661             }
26662             
26663         });
26664     },
26665     
26666     fromLatLngToContainerPixel: function(event)
26667     {
26668         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26669     },
26670     
26671     isApplied: function() 
26672     {
26673         return this.getGmapContext() == false ? false : true;
26674     },
26675     
26676     getGmapContext: function() 
26677     {
26678         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26679     },
26680     
26681     GMapContext: function() 
26682     {
26683         var position = new google.maps.LatLng(this.latitude, this.longitude);
26684         
26685         var _map = new google.maps.Map(this.el.dom, {
26686             center: position,
26687             zoom: this.zoom,
26688             mapTypeId: this.mapTypeId,
26689             mapTypeControl: this.mapTypeControl,
26690             disableDoubleClickZoom: this.disableDoubleClickZoom,
26691             scrollwheel: this.scrollwheel,
26692             streetViewControl: this.streetViewControl,
26693             locationName: this.locationName,
26694             draggable: this.draggable,
26695             enableAutocomplete: this.enableAutocomplete,
26696             enableReverseGeocode: this.enableReverseGeocode
26697         });
26698         
26699         var _marker = new google.maps.Marker({
26700             position: position,
26701             map: _map,
26702             title: this.markerTitle,
26703             draggable: this.draggable
26704         });
26705         
26706         return {
26707             map: _map,
26708             marker: _marker,
26709             circle: null,
26710             location: position,
26711             radius: this.radius,
26712             locationName: this.locationName,
26713             addressComponents: {
26714                 formatted_address: null,
26715                 addressLine1: null,
26716                 addressLine2: null,
26717                 streetName: null,
26718                 streetNumber: null,
26719                 city: null,
26720                 district: null,
26721                 state: null,
26722                 stateOrProvince: null
26723             },
26724             settings: this,
26725             domContainer: this.el.dom,
26726             geodecoder: new google.maps.Geocoder()
26727         };
26728     },
26729     
26730     drawCircle: function(center, radius, options) 
26731     {
26732         if (this.gMapContext.circle != null) {
26733             this.gMapContext.circle.setMap(null);
26734         }
26735         if (radius > 0) {
26736             radius *= 1;
26737             options = Roo.apply({}, options, {
26738                 strokeColor: "#0000FF",
26739                 strokeOpacity: .35,
26740                 strokeWeight: 2,
26741                 fillColor: "#0000FF",
26742                 fillOpacity: .2
26743             });
26744             
26745             options.map = this.gMapContext.map;
26746             options.radius = radius;
26747             options.center = center;
26748             this.gMapContext.circle = new google.maps.Circle(options);
26749             return this.gMapContext.circle;
26750         }
26751         
26752         return null;
26753     },
26754     
26755     setPosition: function(location) 
26756     {
26757         this.gMapContext.location = location;
26758         this.gMapContext.marker.setPosition(location);
26759         this.gMapContext.map.panTo(location);
26760         this.drawCircle(location, this.gMapContext.radius, {});
26761         
26762         var _this = this;
26763         
26764         if (this.gMapContext.settings.enableReverseGeocode) {
26765             this.gMapContext.geodecoder.geocode({
26766                 latLng: this.gMapContext.location
26767             }, function(results, status) {
26768                 
26769                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26770                     _this.gMapContext.locationName = results[0].formatted_address;
26771                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26772                     
26773                     _this.fireEvent('positionchanged', this, location);
26774                 }
26775             });
26776             
26777             return;
26778         }
26779         
26780         this.fireEvent('positionchanged', this, location);
26781     },
26782     
26783     resize: function()
26784     {
26785         google.maps.event.trigger(this.gMapContext.map, "resize");
26786         
26787         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26788         
26789         this.fireEvent('resize', this);
26790     },
26791     
26792     setPositionByLatLng: function(latitude, longitude)
26793     {
26794         this.setPosition(new google.maps.LatLng(latitude, longitude));
26795     },
26796     
26797     getCurrentPosition: function() 
26798     {
26799         return {
26800             latitude: this.gMapContext.location.lat(),
26801             longitude: this.gMapContext.location.lng()
26802         };
26803     },
26804     
26805     getAddressName: function() 
26806     {
26807         return this.gMapContext.locationName;
26808     },
26809     
26810     getAddressComponents: function() 
26811     {
26812         return this.gMapContext.addressComponents;
26813     },
26814     
26815     address_component_from_google_geocode: function(address_components) 
26816     {
26817         var result = {};
26818         
26819         for (var i = 0; i < address_components.length; i++) {
26820             var component = address_components[i];
26821             if (component.types.indexOf("postal_code") >= 0) {
26822                 result.postalCode = component.short_name;
26823             } else if (component.types.indexOf("street_number") >= 0) {
26824                 result.streetNumber = component.short_name;
26825             } else if (component.types.indexOf("route") >= 0) {
26826                 result.streetName = component.short_name;
26827             } else if (component.types.indexOf("neighborhood") >= 0) {
26828                 result.city = component.short_name;
26829             } else if (component.types.indexOf("locality") >= 0) {
26830                 result.city = component.short_name;
26831             } else if (component.types.indexOf("sublocality") >= 0) {
26832                 result.district = component.short_name;
26833             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26834                 result.stateOrProvince = component.short_name;
26835             } else if (component.types.indexOf("country") >= 0) {
26836                 result.country = component.short_name;
26837             }
26838         }
26839         
26840         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26841         result.addressLine2 = "";
26842         return result;
26843     },
26844     
26845     setZoomLevel: function(zoom)
26846     {
26847         this.gMapContext.map.setZoom(zoom);
26848     },
26849     
26850     show: function()
26851     {
26852         if(!this.el){
26853             return;
26854         }
26855         
26856         this.el.show();
26857         
26858         this.resize();
26859         
26860         this.fireEvent('show', this);
26861     },
26862     
26863     hide: function()
26864     {
26865         if(!this.el){
26866             return;
26867         }
26868         
26869         this.el.hide();
26870         
26871         this.fireEvent('hide', this);
26872     }
26873     
26874 });
26875
26876 Roo.apply(Roo.bootstrap.LocationPicker, {
26877     
26878     OverlayView : function(map, options)
26879     {
26880         options = options || {};
26881         
26882         this.setMap(map);
26883     }
26884     
26885     
26886 });/*
26887  * - LGPL
26888  *
26889  * Alert
26890  * 
26891  */
26892
26893 /**
26894  * @class Roo.bootstrap.Alert
26895  * @extends Roo.bootstrap.Component
26896  * Bootstrap Alert class
26897  * @cfg {String} title The title of alert
26898  * @cfg {String} html The content of alert
26899  * @cfg {String} weight (  success | info | warning | danger )
26900  * @cfg {String} faicon font-awesomeicon
26901  * 
26902  * @constructor
26903  * Create a new alert
26904  * @param {Object} config The config object
26905  */
26906
26907
26908 Roo.bootstrap.Alert = function(config){
26909     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26910     
26911 };
26912
26913 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26914     
26915     title: '',
26916     html: '',
26917     weight: false,
26918     faicon: false,
26919     
26920     getAutoCreate : function()
26921     {
26922         
26923         var cfg = {
26924             tag : 'div',
26925             cls : 'alert',
26926             cn : [
26927                 {
26928                     tag : 'i',
26929                     cls : 'roo-alert-icon'
26930                     
26931                 },
26932                 {
26933                     tag : 'b',
26934                     cls : 'roo-alert-title',
26935                     html : this.title
26936                 },
26937                 {
26938                     tag : 'span',
26939                     cls : 'roo-alert-text',
26940                     html : this.html
26941                 }
26942             ]
26943         };
26944         
26945         if(this.faicon){
26946             cfg.cn[0].cls += ' fa ' + this.faicon;
26947         }
26948         
26949         if(this.weight){
26950             cfg.cls += ' alert-' + this.weight;
26951         }
26952         
26953         return cfg;
26954     },
26955     
26956     initEvents: function() 
26957     {
26958         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26959     },
26960     
26961     setTitle : function(str)
26962     {
26963         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26964     },
26965     
26966     setText : function(str)
26967     {
26968         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26969     },
26970     
26971     setWeight : function(weight)
26972     {
26973         if(this.weight){
26974             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26975         }
26976         
26977         this.weight = weight;
26978         
26979         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26980     },
26981     
26982     setIcon : function(icon)
26983     {
26984         if(this.faicon){
26985             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26986         }
26987         
26988         this.faicon = icon;
26989         
26990         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26991     },
26992     
26993     hide: function() 
26994     {
26995         this.el.hide();   
26996     },
26997     
26998     show: function() 
26999     {  
27000         this.el.show();   
27001     }
27002     
27003 });
27004
27005  
27006 /*
27007 * Licence: LGPL
27008 */
27009
27010 /**
27011  * @class Roo.bootstrap.UploadCropbox
27012  * @extends Roo.bootstrap.Component
27013  * Bootstrap UploadCropbox class
27014  * @cfg {String} emptyText show when image has been loaded
27015  * @cfg {String} rotateNotify show when image too small to rotate
27016  * @cfg {Number} errorTimeout default 3000
27017  * @cfg {Number} minWidth default 300
27018  * @cfg {Number} minHeight default 300
27019  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27020  * @cfg {Boolean} isDocument (true|false) default false
27021  * @cfg {String} url action url
27022  * @cfg {String} paramName default 'imageUpload'
27023  * @cfg {String} method default POST
27024  * @cfg {Boolean} loadMask (true|false) default true
27025  * @cfg {Boolean} loadingText default 'Loading...'
27026  * 
27027  * @constructor
27028  * Create a new UploadCropbox
27029  * @param {Object} config The config object
27030  */
27031
27032 Roo.bootstrap.UploadCropbox = function(config){
27033     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27034     
27035     this.addEvents({
27036         /**
27037          * @event beforeselectfile
27038          * Fire before select file
27039          * @param {Roo.bootstrap.UploadCropbox} this
27040          */
27041         "beforeselectfile" : true,
27042         /**
27043          * @event initial
27044          * Fire after initEvent
27045          * @param {Roo.bootstrap.UploadCropbox} this
27046          */
27047         "initial" : true,
27048         /**
27049          * @event crop
27050          * Fire after initEvent
27051          * @param {Roo.bootstrap.UploadCropbox} this
27052          * @param {String} data
27053          */
27054         "crop" : true,
27055         /**
27056          * @event prepare
27057          * Fire when preparing the file data
27058          * @param {Roo.bootstrap.UploadCropbox} this
27059          * @param {Object} file
27060          */
27061         "prepare" : true,
27062         /**
27063          * @event exception
27064          * Fire when get exception
27065          * @param {Roo.bootstrap.UploadCropbox} this
27066          * @param {XMLHttpRequest} xhr
27067          */
27068         "exception" : true,
27069         /**
27070          * @event beforeloadcanvas
27071          * Fire before load the canvas
27072          * @param {Roo.bootstrap.UploadCropbox} this
27073          * @param {String} src
27074          */
27075         "beforeloadcanvas" : true,
27076         /**
27077          * @event trash
27078          * Fire when trash image
27079          * @param {Roo.bootstrap.UploadCropbox} this
27080          */
27081         "trash" : true,
27082         /**
27083          * @event download
27084          * Fire when download the image
27085          * @param {Roo.bootstrap.UploadCropbox} this
27086          */
27087         "download" : true,
27088         /**
27089          * @event footerbuttonclick
27090          * Fire when footerbuttonclick
27091          * @param {Roo.bootstrap.UploadCropbox} this
27092          * @param {String} type
27093          */
27094         "footerbuttonclick" : true,
27095         /**
27096          * @event resize
27097          * Fire when resize
27098          * @param {Roo.bootstrap.UploadCropbox} this
27099          */
27100         "resize" : true,
27101         /**
27102          * @event rotate
27103          * Fire when rotate the image
27104          * @param {Roo.bootstrap.UploadCropbox} this
27105          * @param {String} pos
27106          */
27107         "rotate" : true,
27108         /**
27109          * @event inspect
27110          * Fire when inspect the file
27111          * @param {Roo.bootstrap.UploadCropbox} this
27112          * @param {Object} file
27113          */
27114         "inspect" : true,
27115         /**
27116          * @event upload
27117          * Fire when xhr upload the file
27118          * @param {Roo.bootstrap.UploadCropbox} this
27119          * @param {Object} data
27120          */
27121         "upload" : true,
27122         /**
27123          * @event arrange
27124          * Fire when arrange the file data
27125          * @param {Roo.bootstrap.UploadCropbox} this
27126          * @param {Object} formData
27127          */
27128         "arrange" : true
27129     });
27130     
27131     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27132 };
27133
27134 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27135     
27136     emptyText : 'Click to upload image',
27137     rotateNotify : 'Image is too small to rotate',
27138     errorTimeout : 3000,
27139     scale : 0,
27140     baseScale : 1,
27141     rotate : 0,
27142     dragable : false,
27143     pinching : false,
27144     mouseX : 0,
27145     mouseY : 0,
27146     cropData : false,
27147     minWidth : 300,
27148     minHeight : 300,
27149     file : false,
27150     exif : {},
27151     baseRotate : 1,
27152     cropType : 'image/jpeg',
27153     buttons : false,
27154     canvasLoaded : false,
27155     isDocument : false,
27156     method : 'POST',
27157     paramName : 'imageUpload',
27158     loadMask : true,
27159     loadingText : 'Loading...',
27160     maskEl : false,
27161     
27162     getAutoCreate : function()
27163     {
27164         var cfg = {
27165             tag : 'div',
27166             cls : 'roo-upload-cropbox',
27167             cn : [
27168                 {
27169                     tag : 'input',
27170                     cls : 'roo-upload-cropbox-selector',
27171                     type : 'file'
27172                 },
27173                 {
27174                     tag : 'div',
27175                     cls : 'roo-upload-cropbox-body',
27176                     style : 'cursor:pointer',
27177                     cn : [
27178                         {
27179                             tag : 'div',
27180                             cls : 'roo-upload-cropbox-preview'
27181                         },
27182                         {
27183                             tag : 'div',
27184                             cls : 'roo-upload-cropbox-thumb'
27185                         },
27186                         {
27187                             tag : 'div',
27188                             cls : 'roo-upload-cropbox-empty-notify',
27189                             html : this.emptyText
27190                         },
27191                         {
27192                             tag : 'div',
27193                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27194                             html : this.rotateNotify
27195                         }
27196                     ]
27197                 },
27198                 {
27199                     tag : 'div',
27200                     cls : 'roo-upload-cropbox-footer',
27201                     cn : {
27202                         tag : 'div',
27203                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27204                         cn : []
27205                     }
27206                 }
27207             ]
27208         };
27209         
27210         return cfg;
27211     },
27212     
27213     onRender : function(ct, position)
27214     {
27215         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27216         
27217         if (this.buttons.length) {
27218             
27219             Roo.each(this.buttons, function(bb) {
27220                 
27221                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27222                 
27223                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27224                 
27225             }, this);
27226         }
27227         
27228         if(this.loadMask){
27229             this.maskEl = this.el;
27230         }
27231     },
27232     
27233     initEvents : function()
27234     {
27235         this.urlAPI = (window.createObjectURL && window) || 
27236                                 (window.URL && URL.revokeObjectURL && URL) || 
27237                                 (window.webkitURL && webkitURL);
27238                         
27239         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27240         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27241         
27242         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27243         this.selectorEl.hide();
27244         
27245         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27246         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27247         
27248         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27249         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27250         this.thumbEl.hide();
27251         
27252         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27253         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27254         
27255         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27256         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27257         this.errorEl.hide();
27258         
27259         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27260         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27261         this.footerEl.hide();
27262         
27263         this.setThumbBoxSize();
27264         
27265         this.bind();
27266         
27267         this.resize();
27268         
27269         this.fireEvent('initial', this);
27270     },
27271
27272     bind : function()
27273     {
27274         var _this = this;
27275         
27276         window.addEventListener("resize", function() { _this.resize(); } );
27277         
27278         this.bodyEl.on('click', this.beforeSelectFile, this);
27279         
27280         if(Roo.isTouch){
27281             this.bodyEl.on('touchstart', this.onTouchStart, this);
27282             this.bodyEl.on('touchmove', this.onTouchMove, this);
27283             this.bodyEl.on('touchend', this.onTouchEnd, this);
27284         }
27285         
27286         if(!Roo.isTouch){
27287             this.bodyEl.on('mousedown', this.onMouseDown, this);
27288             this.bodyEl.on('mousemove', this.onMouseMove, this);
27289             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27290             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27291             Roo.get(document).on('mouseup', this.onMouseUp, this);
27292         }
27293         
27294         this.selectorEl.on('change', this.onFileSelected, this);
27295     },
27296     
27297     reset : function()
27298     {    
27299         this.scale = 0;
27300         this.baseScale = 1;
27301         this.rotate = 0;
27302         this.baseRotate = 1;
27303         this.dragable = false;
27304         this.pinching = false;
27305         this.mouseX = 0;
27306         this.mouseY = 0;
27307         this.cropData = false;
27308         this.notifyEl.dom.innerHTML = this.emptyText;
27309         
27310         this.selectorEl.dom.value = '';
27311         
27312     },
27313     
27314     resize : function()
27315     {
27316         if(this.fireEvent('resize', this) != false){
27317             this.setThumbBoxPosition();
27318             this.setCanvasPosition();
27319         }
27320     },
27321     
27322     onFooterButtonClick : function(e, el, o, type)
27323     {
27324         switch (type) {
27325             case 'rotate-left' :
27326                 this.onRotateLeft(e);
27327                 break;
27328             case 'rotate-right' :
27329                 this.onRotateRight(e);
27330                 break;
27331             case 'picture' :
27332                 this.beforeSelectFile(e);
27333                 break;
27334             case 'trash' :
27335                 this.trash(e);
27336                 break;
27337             case 'crop' :
27338                 this.crop(e);
27339                 break;
27340             case 'download' :
27341                 this.download(e);
27342                 break;
27343             default :
27344                 break;
27345         }
27346         
27347         this.fireEvent('footerbuttonclick', this, type);
27348     },
27349     
27350     beforeSelectFile : function(e)
27351     {
27352         e.preventDefault();
27353         
27354         if(this.fireEvent('beforeselectfile', this) != false){
27355             this.selectorEl.dom.click();
27356         }
27357     },
27358     
27359     onFileSelected : function(e)
27360     {
27361         e.preventDefault();
27362         
27363         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27364             return;
27365         }
27366         
27367         var file = this.selectorEl.dom.files[0];
27368         
27369         if(this.fireEvent('inspect', this, file) != false){
27370             this.prepare(file);
27371         }
27372         
27373     },
27374     
27375     trash : function(e)
27376     {
27377         this.fireEvent('trash', this);
27378     },
27379     
27380     download : function(e)
27381     {
27382         this.fireEvent('download', this);
27383     },
27384     
27385     loadCanvas : function(src)
27386     {   
27387         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27388             
27389             this.reset();
27390             
27391             this.imageEl = document.createElement('img');
27392             
27393             var _this = this;
27394             
27395             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27396             
27397             this.imageEl.src = src;
27398         }
27399     },
27400     
27401     onLoadCanvas : function()
27402     {   
27403         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27404         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27405         
27406         this.bodyEl.un('click', this.beforeSelectFile, this);
27407         
27408         this.notifyEl.hide();
27409         this.thumbEl.show();
27410         this.footerEl.show();
27411         
27412         this.baseRotateLevel();
27413         
27414         if(this.isDocument){
27415             this.setThumbBoxSize();
27416         }
27417         
27418         this.setThumbBoxPosition();
27419         
27420         this.baseScaleLevel();
27421         
27422         this.draw();
27423         
27424         this.resize();
27425         
27426         this.canvasLoaded = true;
27427         
27428         if(this.loadMask){
27429             this.maskEl.unmask();
27430         }
27431         
27432     },
27433     
27434     setCanvasPosition : function()
27435     {   
27436         if(!this.canvasEl){
27437             return;
27438         }
27439         
27440         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27441         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27442         
27443         this.previewEl.setLeft(pw);
27444         this.previewEl.setTop(ph);
27445         
27446     },
27447     
27448     onMouseDown : function(e)
27449     {   
27450         e.stopEvent();
27451         
27452         this.dragable = true;
27453         this.pinching = false;
27454         
27455         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27456             this.dragable = false;
27457             return;
27458         }
27459         
27460         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27461         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27462         
27463     },
27464     
27465     onMouseMove : function(e)
27466     {   
27467         e.stopEvent();
27468         
27469         if(!this.canvasLoaded){
27470             return;
27471         }
27472         
27473         if (!this.dragable){
27474             return;
27475         }
27476         
27477         var minX = Math.ceil(this.thumbEl.getLeft(true));
27478         var minY = Math.ceil(this.thumbEl.getTop(true));
27479         
27480         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27481         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27482         
27483         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27484         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27485         
27486         x = x - this.mouseX;
27487         y = y - this.mouseY;
27488         
27489         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27490         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27491         
27492         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27493         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27494         
27495         this.previewEl.setLeft(bgX);
27496         this.previewEl.setTop(bgY);
27497         
27498         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27499         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27500     },
27501     
27502     onMouseUp : function(e)
27503     {   
27504         e.stopEvent();
27505         
27506         this.dragable = false;
27507     },
27508     
27509     onMouseWheel : function(e)
27510     {   
27511         e.stopEvent();
27512         
27513         this.startScale = this.scale;
27514         
27515         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27516         
27517         if(!this.zoomable()){
27518             this.scale = this.startScale;
27519             return;
27520         }
27521         
27522         this.draw();
27523         
27524         return;
27525     },
27526     
27527     zoomable : function()
27528     {
27529         var minScale = this.thumbEl.getWidth() / this.minWidth;
27530         
27531         if(this.minWidth < this.minHeight){
27532             minScale = this.thumbEl.getHeight() / this.minHeight;
27533         }
27534         
27535         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27536         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27537         
27538         if(
27539                 this.isDocument &&
27540                 (this.rotate == 0 || this.rotate == 180) && 
27541                 (
27542                     width > this.imageEl.OriginWidth || 
27543                     height > this.imageEl.OriginHeight ||
27544                     (width < this.minWidth && height < this.minHeight)
27545                 )
27546         ){
27547             return false;
27548         }
27549         
27550         if(
27551                 this.isDocument &&
27552                 (this.rotate == 90 || this.rotate == 270) && 
27553                 (
27554                     width > this.imageEl.OriginWidth || 
27555                     height > this.imageEl.OriginHeight ||
27556                     (width < this.minHeight && height < this.minWidth)
27557                 )
27558         ){
27559             return false;
27560         }
27561         
27562         if(
27563                 !this.isDocument &&
27564                 (this.rotate == 0 || this.rotate == 180) && 
27565                 (
27566                     width < this.minWidth || 
27567                     width > this.imageEl.OriginWidth || 
27568                     height < this.minHeight || 
27569                     height > this.imageEl.OriginHeight
27570                 )
27571         ){
27572             return false;
27573         }
27574         
27575         if(
27576                 !this.isDocument &&
27577                 (this.rotate == 90 || this.rotate == 270) && 
27578                 (
27579                     width < this.minHeight || 
27580                     width > this.imageEl.OriginWidth || 
27581                     height < this.minWidth || 
27582                     height > this.imageEl.OriginHeight
27583                 )
27584         ){
27585             return false;
27586         }
27587         
27588         return true;
27589         
27590     },
27591     
27592     onRotateLeft : function(e)
27593     {   
27594         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27595             
27596             var minScale = this.thumbEl.getWidth() / this.minWidth;
27597             
27598             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27599             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27600             
27601             this.startScale = this.scale;
27602             
27603             while (this.getScaleLevel() < minScale){
27604             
27605                 this.scale = this.scale + 1;
27606                 
27607                 if(!this.zoomable()){
27608                     break;
27609                 }
27610                 
27611                 if(
27612                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27613                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27614                 ){
27615                     continue;
27616                 }
27617                 
27618                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27619
27620                 this.draw();
27621                 
27622                 return;
27623             }
27624             
27625             this.scale = this.startScale;
27626             
27627             this.onRotateFail();
27628             
27629             return false;
27630         }
27631         
27632         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27633
27634         if(this.isDocument){
27635             this.setThumbBoxSize();
27636             this.setThumbBoxPosition();
27637             this.setCanvasPosition();
27638         }
27639         
27640         this.draw();
27641         
27642         this.fireEvent('rotate', this, 'left');
27643         
27644     },
27645     
27646     onRotateRight : function(e)
27647     {
27648         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27649             
27650             var minScale = this.thumbEl.getWidth() / this.minWidth;
27651         
27652             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27653             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27654             
27655             this.startScale = this.scale;
27656             
27657             while (this.getScaleLevel() < minScale){
27658             
27659                 this.scale = this.scale + 1;
27660                 
27661                 if(!this.zoomable()){
27662                     break;
27663                 }
27664                 
27665                 if(
27666                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27667                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27668                 ){
27669                     continue;
27670                 }
27671                 
27672                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27673
27674                 this.draw();
27675                 
27676                 return;
27677             }
27678             
27679             this.scale = this.startScale;
27680             
27681             this.onRotateFail();
27682             
27683             return false;
27684         }
27685         
27686         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27687
27688         if(this.isDocument){
27689             this.setThumbBoxSize();
27690             this.setThumbBoxPosition();
27691             this.setCanvasPosition();
27692         }
27693         
27694         this.draw();
27695         
27696         this.fireEvent('rotate', this, 'right');
27697     },
27698     
27699     onRotateFail : function()
27700     {
27701         this.errorEl.show(true);
27702         
27703         var _this = this;
27704         
27705         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27706     },
27707     
27708     draw : function()
27709     {
27710         this.previewEl.dom.innerHTML = '';
27711         
27712         var canvasEl = document.createElement("canvas");
27713         
27714         var contextEl = canvasEl.getContext("2d");
27715         
27716         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27717         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27718         var center = this.imageEl.OriginWidth / 2;
27719         
27720         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27721             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27722             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27723             center = this.imageEl.OriginHeight / 2;
27724         }
27725         
27726         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27727         
27728         contextEl.translate(center, center);
27729         contextEl.rotate(this.rotate * Math.PI / 180);
27730
27731         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27732         
27733         this.canvasEl = document.createElement("canvas");
27734         
27735         this.contextEl = this.canvasEl.getContext("2d");
27736         
27737         switch (this.rotate) {
27738             case 0 :
27739                 
27740                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27741                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27742                 
27743                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27744                 
27745                 break;
27746             case 90 : 
27747                 
27748                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27749                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27750                 
27751                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27752                     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);
27753                     break;
27754                 }
27755                 
27756                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27757                 
27758                 break;
27759             case 180 :
27760                 
27761                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27762                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27763                 
27764                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27765                     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);
27766                     break;
27767                 }
27768                 
27769                 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);
27770                 
27771                 break;
27772             case 270 :
27773                 
27774                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27775                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27776         
27777                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27778                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27779                     break;
27780                 }
27781                 
27782                 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);
27783                 
27784                 break;
27785             default : 
27786                 break;
27787         }
27788         
27789         this.previewEl.appendChild(this.canvasEl);
27790         
27791         this.setCanvasPosition();
27792     },
27793     
27794     crop : function()
27795     {
27796         if(!this.canvasLoaded){
27797             return;
27798         }
27799         
27800         var imageCanvas = document.createElement("canvas");
27801         
27802         var imageContext = imageCanvas.getContext("2d");
27803         
27804         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27805         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27806         
27807         var center = imageCanvas.width / 2;
27808         
27809         imageContext.translate(center, center);
27810         
27811         imageContext.rotate(this.rotate * Math.PI / 180);
27812         
27813         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27814         
27815         var canvas = document.createElement("canvas");
27816         
27817         var context = canvas.getContext("2d");
27818                 
27819         canvas.width = this.minWidth;
27820         canvas.height = this.minHeight;
27821
27822         switch (this.rotate) {
27823             case 0 :
27824                 
27825                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27826                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27827                 
27828                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27829                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27830                 
27831                 var targetWidth = this.minWidth - 2 * x;
27832                 var targetHeight = this.minHeight - 2 * y;
27833                 
27834                 var scale = 1;
27835                 
27836                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27837                     scale = targetWidth / width;
27838                 }
27839                 
27840                 if(x > 0 && y == 0){
27841                     scale = targetHeight / height;
27842                 }
27843                 
27844                 if(x > 0 && y > 0){
27845                     scale = targetWidth / width;
27846                     
27847                     if(width < height){
27848                         scale = targetHeight / height;
27849                     }
27850                 }
27851                 
27852                 context.scale(scale, scale);
27853                 
27854                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27855                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27856
27857                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27858                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27859
27860                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27861                 
27862                 break;
27863             case 90 : 
27864                 
27865                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27866                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27867                 
27868                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27869                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27870                 
27871                 var targetWidth = this.minWidth - 2 * x;
27872                 var targetHeight = this.minHeight - 2 * y;
27873                 
27874                 var scale = 1;
27875                 
27876                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27877                     scale = targetWidth / width;
27878                 }
27879                 
27880                 if(x > 0 && y == 0){
27881                     scale = targetHeight / height;
27882                 }
27883                 
27884                 if(x > 0 && y > 0){
27885                     scale = targetWidth / width;
27886                     
27887                     if(width < height){
27888                         scale = targetHeight / height;
27889                     }
27890                 }
27891                 
27892                 context.scale(scale, scale);
27893                 
27894                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27895                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27896
27897                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27898                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27899                 
27900                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27901                 
27902                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27903                 
27904                 break;
27905             case 180 :
27906                 
27907                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27908                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27909                 
27910                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27911                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27912                 
27913                 var targetWidth = this.minWidth - 2 * x;
27914                 var targetHeight = this.minHeight - 2 * y;
27915                 
27916                 var scale = 1;
27917                 
27918                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27919                     scale = targetWidth / width;
27920                 }
27921                 
27922                 if(x > 0 && y == 0){
27923                     scale = targetHeight / height;
27924                 }
27925                 
27926                 if(x > 0 && y > 0){
27927                     scale = targetWidth / width;
27928                     
27929                     if(width < height){
27930                         scale = targetHeight / height;
27931                     }
27932                 }
27933                 
27934                 context.scale(scale, scale);
27935                 
27936                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27937                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27938
27939                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27940                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27941
27942                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27943                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27944                 
27945                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27946                 
27947                 break;
27948             case 270 :
27949                 
27950                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27951                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27952                 
27953                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27954                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27955                 
27956                 var targetWidth = this.minWidth - 2 * x;
27957                 var targetHeight = this.minHeight - 2 * y;
27958                 
27959                 var scale = 1;
27960                 
27961                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27962                     scale = targetWidth / width;
27963                 }
27964                 
27965                 if(x > 0 && y == 0){
27966                     scale = targetHeight / height;
27967                 }
27968                 
27969                 if(x > 0 && y > 0){
27970                     scale = targetWidth / width;
27971                     
27972                     if(width < height){
27973                         scale = targetHeight / height;
27974                     }
27975                 }
27976                 
27977                 context.scale(scale, scale);
27978                 
27979                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27980                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27981
27982                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27983                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27984                 
27985                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27986                 
27987                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27988                 
27989                 break;
27990             default : 
27991                 break;
27992         }
27993         
27994         this.cropData = canvas.toDataURL(this.cropType);
27995         
27996         if(this.fireEvent('crop', this, this.cropData) !== false){
27997             this.process(this.file, this.cropData);
27998         }
27999         
28000         return;
28001         
28002     },
28003     
28004     setThumbBoxSize : function()
28005     {
28006         var width, height;
28007         
28008         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28009             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28010             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28011             
28012             this.minWidth = width;
28013             this.minHeight = height;
28014             
28015             if(this.rotate == 90 || this.rotate == 270){
28016                 this.minWidth = height;
28017                 this.minHeight = width;
28018             }
28019         }
28020         
28021         height = 300;
28022         width = Math.ceil(this.minWidth * height / this.minHeight);
28023         
28024         if(this.minWidth > this.minHeight){
28025             width = 300;
28026             height = Math.ceil(this.minHeight * width / this.minWidth);
28027         }
28028         
28029         this.thumbEl.setStyle({
28030             width : width + 'px',
28031             height : height + 'px'
28032         });
28033
28034         return;
28035             
28036     },
28037     
28038     setThumbBoxPosition : function()
28039     {
28040         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28041         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28042         
28043         this.thumbEl.setLeft(x);
28044         this.thumbEl.setTop(y);
28045         
28046     },
28047     
28048     baseRotateLevel : function()
28049     {
28050         this.baseRotate = 1;
28051         
28052         if(
28053                 typeof(this.exif) != 'undefined' &&
28054                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28055                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28056         ){
28057             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28058         }
28059         
28060         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28061         
28062     },
28063     
28064     baseScaleLevel : function()
28065     {
28066         var width, height;
28067         
28068         if(this.isDocument){
28069             
28070             if(this.baseRotate == 6 || this.baseRotate == 8){
28071             
28072                 height = this.thumbEl.getHeight();
28073                 this.baseScale = height / this.imageEl.OriginWidth;
28074
28075                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28076                     width = this.thumbEl.getWidth();
28077                     this.baseScale = width / this.imageEl.OriginHeight;
28078                 }
28079
28080                 return;
28081             }
28082
28083             height = this.thumbEl.getHeight();
28084             this.baseScale = height / this.imageEl.OriginHeight;
28085
28086             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28087                 width = this.thumbEl.getWidth();
28088                 this.baseScale = width / this.imageEl.OriginWidth;
28089             }
28090
28091             return;
28092         }
28093         
28094         if(this.baseRotate == 6 || this.baseRotate == 8){
28095             
28096             width = this.thumbEl.getHeight();
28097             this.baseScale = width / this.imageEl.OriginHeight;
28098             
28099             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28100                 height = this.thumbEl.getWidth();
28101                 this.baseScale = height / this.imageEl.OriginHeight;
28102             }
28103             
28104             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28105                 height = this.thumbEl.getWidth();
28106                 this.baseScale = height / this.imageEl.OriginHeight;
28107                 
28108                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28109                     width = this.thumbEl.getHeight();
28110                     this.baseScale = width / this.imageEl.OriginWidth;
28111                 }
28112             }
28113             
28114             return;
28115         }
28116         
28117         width = this.thumbEl.getWidth();
28118         this.baseScale = width / this.imageEl.OriginWidth;
28119         
28120         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28121             height = this.thumbEl.getHeight();
28122             this.baseScale = height / this.imageEl.OriginHeight;
28123         }
28124         
28125         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28126             
28127             height = this.thumbEl.getHeight();
28128             this.baseScale = height / this.imageEl.OriginHeight;
28129             
28130             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28131                 width = this.thumbEl.getWidth();
28132                 this.baseScale = width / this.imageEl.OriginWidth;
28133             }
28134             
28135         }
28136         
28137         return;
28138     },
28139     
28140     getScaleLevel : function()
28141     {
28142         return this.baseScale * Math.pow(1.1, this.scale);
28143     },
28144     
28145     onTouchStart : function(e)
28146     {
28147         if(!this.canvasLoaded){
28148             this.beforeSelectFile(e);
28149             return;
28150         }
28151         
28152         var touches = e.browserEvent.touches;
28153         
28154         if(!touches){
28155             return;
28156         }
28157         
28158         if(touches.length == 1){
28159             this.onMouseDown(e);
28160             return;
28161         }
28162         
28163         if(touches.length != 2){
28164             return;
28165         }
28166         
28167         var coords = [];
28168         
28169         for(var i = 0, finger; finger = touches[i]; i++){
28170             coords.push(finger.pageX, finger.pageY);
28171         }
28172         
28173         var x = Math.pow(coords[0] - coords[2], 2);
28174         var y = Math.pow(coords[1] - coords[3], 2);
28175         
28176         this.startDistance = Math.sqrt(x + y);
28177         
28178         this.startScale = this.scale;
28179         
28180         this.pinching = true;
28181         this.dragable = false;
28182         
28183     },
28184     
28185     onTouchMove : function(e)
28186     {
28187         if(!this.pinching && !this.dragable){
28188             return;
28189         }
28190         
28191         var touches = e.browserEvent.touches;
28192         
28193         if(!touches){
28194             return;
28195         }
28196         
28197         if(this.dragable){
28198             this.onMouseMove(e);
28199             return;
28200         }
28201         
28202         var coords = [];
28203         
28204         for(var i = 0, finger; finger = touches[i]; i++){
28205             coords.push(finger.pageX, finger.pageY);
28206         }
28207         
28208         var x = Math.pow(coords[0] - coords[2], 2);
28209         var y = Math.pow(coords[1] - coords[3], 2);
28210         
28211         this.endDistance = Math.sqrt(x + y);
28212         
28213         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28214         
28215         if(!this.zoomable()){
28216             this.scale = this.startScale;
28217             return;
28218         }
28219         
28220         this.draw();
28221         
28222     },
28223     
28224     onTouchEnd : function(e)
28225     {
28226         this.pinching = false;
28227         this.dragable = false;
28228         
28229     },
28230     
28231     process : function(file, crop)
28232     {
28233         if(this.loadMask){
28234             this.maskEl.mask(this.loadingText);
28235         }
28236         
28237         this.xhr = new XMLHttpRequest();
28238         
28239         file.xhr = this.xhr;
28240
28241         this.xhr.open(this.method, this.url, true);
28242         
28243         var headers = {
28244             "Accept": "application/json",
28245             "Cache-Control": "no-cache",
28246             "X-Requested-With": "XMLHttpRequest"
28247         };
28248         
28249         for (var headerName in headers) {
28250             var headerValue = headers[headerName];
28251             if (headerValue) {
28252                 this.xhr.setRequestHeader(headerName, headerValue);
28253             }
28254         }
28255         
28256         var _this = this;
28257         
28258         this.xhr.onload = function()
28259         {
28260             _this.xhrOnLoad(_this.xhr);
28261         }
28262         
28263         this.xhr.onerror = function()
28264         {
28265             _this.xhrOnError(_this.xhr);
28266         }
28267         
28268         var formData = new FormData();
28269
28270         formData.append('returnHTML', 'NO');
28271         
28272         if(crop){
28273             formData.append('crop', crop);
28274         }
28275         
28276         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28277             formData.append(this.paramName, file, file.name);
28278         }
28279         
28280         if(typeof(file.filename) != 'undefined'){
28281             formData.append('filename', file.filename);
28282         }
28283         
28284         if(typeof(file.mimetype) != 'undefined'){
28285             formData.append('mimetype', file.mimetype);
28286         }
28287         
28288         if(this.fireEvent('arrange', this, formData) != false){
28289             this.xhr.send(formData);
28290         };
28291     },
28292     
28293     xhrOnLoad : function(xhr)
28294     {
28295         if(this.loadMask){
28296             this.maskEl.unmask();
28297         }
28298         
28299         if (xhr.readyState !== 4) {
28300             this.fireEvent('exception', this, xhr);
28301             return;
28302         }
28303
28304         var response = Roo.decode(xhr.responseText);
28305         
28306         if(!response.success){
28307             this.fireEvent('exception', this, xhr);
28308             return;
28309         }
28310         
28311         var response = Roo.decode(xhr.responseText);
28312         
28313         this.fireEvent('upload', this, response);
28314         
28315     },
28316     
28317     xhrOnError : function()
28318     {
28319         if(this.loadMask){
28320             this.maskEl.unmask();
28321         }
28322         
28323         Roo.log('xhr on error');
28324         
28325         var response = Roo.decode(xhr.responseText);
28326           
28327         Roo.log(response);
28328         
28329     },
28330     
28331     prepare : function(file)
28332     {   
28333         if(this.loadMask){
28334             this.maskEl.mask(this.loadingText);
28335         }
28336         
28337         this.file = false;
28338         this.exif = {};
28339         
28340         if(typeof(file) === 'string'){
28341             this.loadCanvas(file);
28342             return;
28343         }
28344         
28345         if(!file || !this.urlAPI){
28346             return;
28347         }
28348         
28349         this.file = file;
28350         this.cropType = file.type;
28351         
28352         var _this = this;
28353         
28354         if(this.fireEvent('prepare', this, this.file) != false){
28355             
28356             var reader = new FileReader();
28357             
28358             reader.onload = function (e) {
28359                 if (e.target.error) {
28360                     Roo.log(e.target.error);
28361                     return;
28362                 }
28363                 
28364                 var buffer = e.target.result,
28365                     dataView = new DataView(buffer),
28366                     offset = 2,
28367                     maxOffset = dataView.byteLength - 4,
28368                     markerBytes,
28369                     markerLength;
28370                 
28371                 if (dataView.getUint16(0) === 0xffd8) {
28372                     while (offset < maxOffset) {
28373                         markerBytes = dataView.getUint16(offset);
28374                         
28375                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28376                             markerLength = dataView.getUint16(offset + 2) + 2;
28377                             if (offset + markerLength > dataView.byteLength) {
28378                                 Roo.log('Invalid meta data: Invalid segment size.');
28379                                 break;
28380                             }
28381                             
28382                             if(markerBytes == 0xffe1){
28383                                 _this.parseExifData(
28384                                     dataView,
28385                                     offset,
28386                                     markerLength
28387                                 );
28388                             }
28389                             
28390                             offset += markerLength;
28391                             
28392                             continue;
28393                         }
28394                         
28395                         break;
28396                     }
28397                     
28398                 }
28399                 
28400                 var url = _this.urlAPI.createObjectURL(_this.file);
28401                 
28402                 _this.loadCanvas(url);
28403                 
28404                 return;
28405             }
28406             
28407             reader.readAsArrayBuffer(this.file);
28408             
28409         }
28410         
28411     },
28412     
28413     parseExifData : function(dataView, offset, length)
28414     {
28415         var tiffOffset = offset + 10,
28416             littleEndian,
28417             dirOffset;
28418     
28419         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28420             // No Exif data, might be XMP data instead
28421             return;
28422         }
28423         
28424         // Check for the ASCII code for "Exif" (0x45786966):
28425         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28426             // No Exif data, might be XMP data instead
28427             return;
28428         }
28429         if (tiffOffset + 8 > dataView.byteLength) {
28430             Roo.log('Invalid Exif data: Invalid segment size.');
28431             return;
28432         }
28433         // Check for the two null bytes:
28434         if (dataView.getUint16(offset + 8) !== 0x0000) {
28435             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28436             return;
28437         }
28438         // Check the byte alignment:
28439         switch (dataView.getUint16(tiffOffset)) {
28440         case 0x4949:
28441             littleEndian = true;
28442             break;
28443         case 0x4D4D:
28444             littleEndian = false;
28445             break;
28446         default:
28447             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28448             return;
28449         }
28450         // Check for the TIFF tag marker (0x002A):
28451         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28452             Roo.log('Invalid Exif data: Missing TIFF marker.');
28453             return;
28454         }
28455         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28456         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28457         
28458         this.parseExifTags(
28459             dataView,
28460             tiffOffset,
28461             tiffOffset + dirOffset,
28462             littleEndian
28463         );
28464     },
28465     
28466     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28467     {
28468         var tagsNumber,
28469             dirEndOffset,
28470             i;
28471         if (dirOffset + 6 > dataView.byteLength) {
28472             Roo.log('Invalid Exif data: Invalid directory offset.');
28473             return;
28474         }
28475         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28476         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28477         if (dirEndOffset + 4 > dataView.byteLength) {
28478             Roo.log('Invalid Exif data: Invalid directory size.');
28479             return;
28480         }
28481         for (i = 0; i < tagsNumber; i += 1) {
28482             this.parseExifTag(
28483                 dataView,
28484                 tiffOffset,
28485                 dirOffset + 2 + 12 * i, // tag offset
28486                 littleEndian
28487             );
28488         }
28489         // Return the offset to the next directory:
28490         return dataView.getUint32(dirEndOffset, littleEndian);
28491     },
28492     
28493     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28494     {
28495         var tag = dataView.getUint16(offset, littleEndian);
28496         
28497         this.exif[tag] = this.getExifValue(
28498             dataView,
28499             tiffOffset,
28500             offset,
28501             dataView.getUint16(offset + 2, littleEndian), // tag type
28502             dataView.getUint32(offset + 4, littleEndian), // tag length
28503             littleEndian
28504         );
28505     },
28506     
28507     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28508     {
28509         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28510             tagSize,
28511             dataOffset,
28512             values,
28513             i,
28514             str,
28515             c;
28516     
28517         if (!tagType) {
28518             Roo.log('Invalid Exif data: Invalid tag type.');
28519             return;
28520         }
28521         
28522         tagSize = tagType.size * length;
28523         // Determine if the value is contained in the dataOffset bytes,
28524         // or if the value at the dataOffset is a pointer to the actual data:
28525         dataOffset = tagSize > 4 ?
28526                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28527         if (dataOffset + tagSize > dataView.byteLength) {
28528             Roo.log('Invalid Exif data: Invalid data offset.');
28529             return;
28530         }
28531         if (length === 1) {
28532             return tagType.getValue(dataView, dataOffset, littleEndian);
28533         }
28534         values = [];
28535         for (i = 0; i < length; i += 1) {
28536             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28537         }
28538         
28539         if (tagType.ascii) {
28540             str = '';
28541             // Concatenate the chars:
28542             for (i = 0; i < values.length; i += 1) {
28543                 c = values[i];
28544                 // Ignore the terminating NULL byte(s):
28545                 if (c === '\u0000') {
28546                     break;
28547                 }
28548                 str += c;
28549             }
28550             return str;
28551         }
28552         return values;
28553     }
28554     
28555 });
28556
28557 Roo.apply(Roo.bootstrap.UploadCropbox, {
28558     tags : {
28559         'Orientation': 0x0112
28560     },
28561     
28562     Orientation: {
28563             1: 0, //'top-left',
28564 //            2: 'top-right',
28565             3: 180, //'bottom-right',
28566 //            4: 'bottom-left',
28567 //            5: 'left-top',
28568             6: 90, //'right-top',
28569 //            7: 'right-bottom',
28570             8: 270 //'left-bottom'
28571     },
28572     
28573     exifTagTypes : {
28574         // byte, 8-bit unsigned int:
28575         1: {
28576             getValue: function (dataView, dataOffset) {
28577                 return dataView.getUint8(dataOffset);
28578             },
28579             size: 1
28580         },
28581         // ascii, 8-bit byte:
28582         2: {
28583             getValue: function (dataView, dataOffset) {
28584                 return String.fromCharCode(dataView.getUint8(dataOffset));
28585             },
28586             size: 1,
28587             ascii: true
28588         },
28589         // short, 16 bit int:
28590         3: {
28591             getValue: function (dataView, dataOffset, littleEndian) {
28592                 return dataView.getUint16(dataOffset, littleEndian);
28593             },
28594             size: 2
28595         },
28596         // long, 32 bit int:
28597         4: {
28598             getValue: function (dataView, dataOffset, littleEndian) {
28599                 return dataView.getUint32(dataOffset, littleEndian);
28600             },
28601             size: 4
28602         },
28603         // rational = two long values, first is numerator, second is denominator:
28604         5: {
28605             getValue: function (dataView, dataOffset, littleEndian) {
28606                 return dataView.getUint32(dataOffset, littleEndian) /
28607                     dataView.getUint32(dataOffset + 4, littleEndian);
28608             },
28609             size: 8
28610         },
28611         // slong, 32 bit signed int:
28612         9: {
28613             getValue: function (dataView, dataOffset, littleEndian) {
28614                 return dataView.getInt32(dataOffset, littleEndian);
28615             },
28616             size: 4
28617         },
28618         // srational, two slongs, first is numerator, second is denominator:
28619         10: {
28620             getValue: function (dataView, dataOffset, littleEndian) {
28621                 return dataView.getInt32(dataOffset, littleEndian) /
28622                     dataView.getInt32(dataOffset + 4, littleEndian);
28623             },
28624             size: 8
28625         }
28626     },
28627     
28628     footer : {
28629         STANDARD : [
28630             {
28631                 tag : 'div',
28632                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28633                 action : 'rotate-left',
28634                 cn : [
28635                     {
28636                         tag : 'button',
28637                         cls : 'btn btn-default',
28638                         html : '<i class="fa fa-undo"></i>'
28639                     }
28640                 ]
28641             },
28642             {
28643                 tag : 'div',
28644                 cls : 'btn-group roo-upload-cropbox-picture',
28645                 action : 'picture',
28646                 cn : [
28647                     {
28648                         tag : 'button',
28649                         cls : 'btn btn-default',
28650                         html : '<i class="fa fa-picture-o"></i>'
28651                     }
28652                 ]
28653             },
28654             {
28655                 tag : 'div',
28656                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28657                 action : 'rotate-right',
28658                 cn : [
28659                     {
28660                         tag : 'button',
28661                         cls : 'btn btn-default',
28662                         html : '<i class="fa fa-repeat"></i>'
28663                     }
28664                 ]
28665             }
28666         ],
28667         DOCUMENT : [
28668             {
28669                 tag : 'div',
28670                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28671                 action : 'rotate-left',
28672                 cn : [
28673                     {
28674                         tag : 'button',
28675                         cls : 'btn btn-default',
28676                         html : '<i class="fa fa-undo"></i>'
28677                     }
28678                 ]
28679             },
28680             {
28681                 tag : 'div',
28682                 cls : 'btn-group roo-upload-cropbox-download',
28683                 action : 'download',
28684                 cn : [
28685                     {
28686                         tag : 'button',
28687                         cls : 'btn btn-default',
28688                         html : '<i class="fa fa-download"></i>'
28689                     }
28690                 ]
28691             },
28692             {
28693                 tag : 'div',
28694                 cls : 'btn-group roo-upload-cropbox-crop',
28695                 action : 'crop',
28696                 cn : [
28697                     {
28698                         tag : 'button',
28699                         cls : 'btn btn-default',
28700                         html : '<i class="fa fa-crop"></i>'
28701                     }
28702                 ]
28703             },
28704             {
28705                 tag : 'div',
28706                 cls : 'btn-group roo-upload-cropbox-trash',
28707                 action : 'trash',
28708                 cn : [
28709                     {
28710                         tag : 'button',
28711                         cls : 'btn btn-default',
28712                         html : '<i class="fa fa-trash"></i>'
28713                     }
28714                 ]
28715             },
28716             {
28717                 tag : 'div',
28718                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28719                 action : 'rotate-right',
28720                 cn : [
28721                     {
28722                         tag : 'button',
28723                         cls : 'btn btn-default',
28724                         html : '<i class="fa fa-repeat"></i>'
28725                     }
28726                 ]
28727             }
28728         ],
28729         ROTATOR : [
28730             {
28731                 tag : 'div',
28732                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28733                 action : 'rotate-left',
28734                 cn : [
28735                     {
28736                         tag : 'button',
28737                         cls : 'btn btn-default',
28738                         html : '<i class="fa fa-undo"></i>'
28739                     }
28740                 ]
28741             },
28742             {
28743                 tag : 'div',
28744                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28745                 action : 'rotate-right',
28746                 cn : [
28747                     {
28748                         tag : 'button',
28749                         cls : 'btn btn-default',
28750                         html : '<i class="fa fa-repeat"></i>'
28751                     }
28752                 ]
28753             }
28754         ]
28755     }
28756 });
28757
28758 /*
28759 * Licence: LGPL
28760 */
28761
28762 /**
28763  * @class Roo.bootstrap.DocumentManager
28764  * @extends Roo.bootstrap.Component
28765  * Bootstrap DocumentManager class
28766  * @cfg {String} paramName default 'imageUpload'
28767  * @cfg {String} toolTipName default 'filename'
28768  * @cfg {String} method default POST
28769  * @cfg {String} url action url
28770  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28771  * @cfg {Boolean} multiple multiple upload default true
28772  * @cfg {Number} thumbSize default 300
28773  * @cfg {String} fieldLabel
28774  * @cfg {Number} labelWidth default 4
28775  * @cfg {String} labelAlign (left|top) default left
28776  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28777 * @cfg {Number} labellg set the width of label (1-12)
28778  * @cfg {Number} labelmd set the width of label (1-12)
28779  * @cfg {Number} labelsm set the width of label (1-12)
28780  * @cfg {Number} labelxs set the width of label (1-12)
28781  * 
28782  * @constructor
28783  * Create a new DocumentManager
28784  * @param {Object} config The config object
28785  */
28786
28787 Roo.bootstrap.DocumentManager = function(config){
28788     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28789     
28790     this.files = [];
28791     this.delegates = [];
28792     
28793     this.addEvents({
28794         /**
28795          * @event initial
28796          * Fire when initial the DocumentManager
28797          * @param {Roo.bootstrap.DocumentManager} this
28798          */
28799         "initial" : true,
28800         /**
28801          * @event inspect
28802          * inspect selected file
28803          * @param {Roo.bootstrap.DocumentManager} this
28804          * @param {File} file
28805          */
28806         "inspect" : true,
28807         /**
28808          * @event exception
28809          * Fire when xhr load exception
28810          * @param {Roo.bootstrap.DocumentManager} this
28811          * @param {XMLHttpRequest} xhr
28812          */
28813         "exception" : true,
28814         /**
28815          * @event afterupload
28816          * Fire when xhr load exception
28817          * @param {Roo.bootstrap.DocumentManager} this
28818          * @param {XMLHttpRequest} xhr
28819          */
28820         "afterupload" : true,
28821         /**
28822          * @event prepare
28823          * prepare the form data
28824          * @param {Roo.bootstrap.DocumentManager} this
28825          * @param {Object} formData
28826          */
28827         "prepare" : true,
28828         /**
28829          * @event remove
28830          * Fire when remove the file
28831          * @param {Roo.bootstrap.DocumentManager} this
28832          * @param {Object} file
28833          */
28834         "remove" : true,
28835         /**
28836          * @event refresh
28837          * Fire after refresh the file
28838          * @param {Roo.bootstrap.DocumentManager} this
28839          */
28840         "refresh" : true,
28841         /**
28842          * @event click
28843          * Fire after click the image
28844          * @param {Roo.bootstrap.DocumentManager} this
28845          * @param {Object} file
28846          */
28847         "click" : true,
28848         /**
28849          * @event edit
28850          * Fire when upload a image and editable set to true
28851          * @param {Roo.bootstrap.DocumentManager} this
28852          * @param {Object} file
28853          */
28854         "edit" : true,
28855         /**
28856          * @event beforeselectfile
28857          * Fire before select file
28858          * @param {Roo.bootstrap.DocumentManager} this
28859          */
28860         "beforeselectfile" : true,
28861         /**
28862          * @event process
28863          * Fire before process file
28864          * @param {Roo.bootstrap.DocumentManager} this
28865          * @param {Object} file
28866          */
28867         "process" : true,
28868         /**
28869          * @event previewrendered
28870          * Fire when preview rendered
28871          * @param {Roo.bootstrap.DocumentManager} this
28872          * @param {Object} file
28873          */
28874         "previewrendered" : true,
28875         /**
28876          */
28877         "previewResize" : true
28878         
28879     });
28880 };
28881
28882 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28883     
28884     boxes : 0,
28885     inputName : '',
28886     thumbSize : 300,
28887     multiple : true,
28888     files : false,
28889     method : 'POST',
28890     url : '',
28891     paramName : 'imageUpload',
28892     toolTipName : 'filename',
28893     fieldLabel : '',
28894     labelWidth : 4,
28895     labelAlign : 'left',
28896     editable : true,
28897     delegates : false,
28898     xhr : false, 
28899     
28900     labellg : 0,
28901     labelmd : 0,
28902     labelsm : 0,
28903     labelxs : 0,
28904     
28905     getAutoCreate : function()
28906     {   
28907         var managerWidget = {
28908             tag : 'div',
28909             cls : 'roo-document-manager',
28910             cn : [
28911                 {
28912                     tag : 'input',
28913                     cls : 'roo-document-manager-selector',
28914                     type : 'file'
28915                 },
28916                 {
28917                     tag : 'div',
28918                     cls : 'roo-document-manager-uploader',
28919                     cn : [
28920                         {
28921                             tag : 'div',
28922                             cls : 'roo-document-manager-upload-btn',
28923                             html : '<i class="fa fa-plus"></i>'
28924                         }
28925                     ]
28926                     
28927                 }
28928             ]
28929         };
28930         
28931         var content = [
28932             {
28933                 tag : 'div',
28934                 cls : 'column col-md-12',
28935                 cn : managerWidget
28936             }
28937         ];
28938         
28939         if(this.fieldLabel.length){
28940             
28941             content = [
28942                 {
28943                     tag : 'div',
28944                     cls : 'column col-md-12',
28945                     html : this.fieldLabel
28946                 },
28947                 {
28948                     tag : 'div',
28949                     cls : 'column col-md-12',
28950                     cn : managerWidget
28951                 }
28952             ];
28953
28954             if(this.labelAlign == 'left'){
28955                 content = [
28956                     {
28957                         tag : 'div',
28958                         cls : 'column',
28959                         html : this.fieldLabel
28960                     },
28961                     {
28962                         tag : 'div',
28963                         cls : 'column',
28964                         cn : managerWidget
28965                     }
28966                 ];
28967                 
28968                 if(this.labelWidth > 12){
28969                     content[0].style = "width: " + this.labelWidth + 'px';
28970                 }
28971
28972                 if(this.labelWidth < 13 && this.labelmd == 0){
28973                     this.labelmd = this.labelWidth;
28974                 }
28975
28976                 if(this.labellg > 0){
28977                     content[0].cls += ' col-lg-' + this.labellg;
28978                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28979                 }
28980
28981                 if(this.labelmd > 0){
28982                     content[0].cls += ' col-md-' + this.labelmd;
28983                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28984                 }
28985
28986                 if(this.labelsm > 0){
28987                     content[0].cls += ' col-sm-' + this.labelsm;
28988                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28989                 }
28990
28991                 if(this.labelxs > 0){
28992                     content[0].cls += ' col-xs-' + this.labelxs;
28993                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28994                 }
28995                 
28996             }
28997         }
28998         
28999         var cfg = {
29000             tag : 'div',
29001             cls : 'row clearfix',
29002             cn : content
29003         };
29004         
29005         return cfg;
29006         
29007     },
29008     
29009     initEvents : function()
29010     {
29011         this.managerEl = this.el.select('.roo-document-manager', true).first();
29012         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29013         
29014         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29015         this.selectorEl.hide();
29016         
29017         if(this.multiple){
29018             this.selectorEl.attr('multiple', 'multiple');
29019         }
29020         
29021         this.selectorEl.on('change', this.onFileSelected, this);
29022         
29023         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29024         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29025         
29026         this.uploader.on('click', this.onUploaderClick, this);
29027         
29028         this.renderProgressDialog();
29029         
29030         var _this = this;
29031         
29032         window.addEventListener("resize", function() { _this.refresh(); } );
29033         
29034         this.fireEvent('initial', this);
29035     },
29036     
29037     renderProgressDialog : function()
29038     {
29039         var _this = this;
29040         
29041         this.progressDialog = new Roo.bootstrap.Modal({
29042             cls : 'roo-document-manager-progress-dialog',
29043             allow_close : false,
29044             title : '',
29045             buttons : [
29046                 {
29047                     name  :'cancel',
29048                     weight : 'danger',
29049                     html : 'Cancel'
29050                 }
29051             ], 
29052             listeners : { 
29053                 btnclick : function() {
29054                     _this.uploadCancel();
29055                     this.hide();
29056                 }
29057             }
29058         });
29059          
29060         this.progressDialog.render(Roo.get(document.body));
29061          
29062         this.progress = new Roo.bootstrap.Progress({
29063             cls : 'roo-document-manager-progress',
29064             active : true,
29065             striped : true
29066         });
29067         
29068         this.progress.render(this.progressDialog.getChildContainer());
29069         
29070         this.progressBar = new Roo.bootstrap.ProgressBar({
29071             cls : 'roo-document-manager-progress-bar',
29072             aria_valuenow : 0,
29073             aria_valuemin : 0,
29074             aria_valuemax : 12,
29075             panel : 'success'
29076         });
29077         
29078         this.progressBar.render(this.progress.getChildContainer());
29079     },
29080     
29081     onUploaderClick : function(e)
29082     {
29083         e.preventDefault();
29084      
29085         if(this.fireEvent('beforeselectfile', this) != false){
29086             this.selectorEl.dom.click();
29087         }
29088         
29089     },
29090     
29091     onFileSelected : function(e)
29092     {
29093         e.preventDefault();
29094         
29095         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29096             return;
29097         }
29098         
29099         Roo.each(this.selectorEl.dom.files, function(file){
29100             if(this.fireEvent('inspect', this, file) != false){
29101                 this.files.push(file);
29102             }
29103         }, this);
29104         
29105         this.queue();
29106         
29107     },
29108     
29109     queue : function()
29110     {
29111         this.selectorEl.dom.value = '';
29112         
29113         if(!this.files || !this.files.length){
29114             return;
29115         }
29116         
29117         if(this.boxes > 0 && this.files.length > this.boxes){
29118             this.files = this.files.slice(0, this.boxes);
29119         }
29120         
29121         this.uploader.show();
29122         
29123         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29124             this.uploader.hide();
29125         }
29126         
29127         var _this = this;
29128         
29129         var files = [];
29130         
29131         var docs = [];
29132         
29133         Roo.each(this.files, function(file){
29134             
29135             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29136                 var f = this.renderPreview(file);
29137                 files.push(f);
29138                 return;
29139             }
29140             
29141             if(file.type.indexOf('image') != -1){
29142                 this.delegates.push(
29143                     (function(){
29144                         _this.process(file);
29145                     }).createDelegate(this)
29146                 );
29147         
29148                 return;
29149             }
29150             
29151             docs.push(
29152                 (function(){
29153                     _this.process(file);
29154                 }).createDelegate(this)
29155             );
29156             
29157         }, this);
29158         
29159         this.files = files;
29160         
29161         this.delegates = this.delegates.concat(docs);
29162         
29163         if(!this.delegates.length){
29164             this.refresh();
29165             return;
29166         }
29167         
29168         this.progressBar.aria_valuemax = this.delegates.length;
29169         
29170         this.arrange();
29171         
29172         return;
29173     },
29174     
29175     arrange : function()
29176     {
29177         if(!this.delegates.length){
29178             this.progressDialog.hide();
29179             this.refresh();
29180             return;
29181         }
29182         
29183         var delegate = this.delegates.shift();
29184         
29185         this.progressDialog.show();
29186         
29187         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29188         
29189         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29190         
29191         delegate();
29192     },
29193     
29194     refresh : function()
29195     {
29196         this.uploader.show();
29197         
29198         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29199             this.uploader.hide();
29200         }
29201         
29202         Roo.isTouch ? this.closable(false) : this.closable(true);
29203         
29204         this.fireEvent('refresh', this);
29205     },
29206     
29207     onRemove : function(e, el, o)
29208     {
29209         e.preventDefault();
29210         
29211         this.fireEvent('remove', this, o);
29212         
29213     },
29214     
29215     remove : function(o)
29216     {
29217         var files = [];
29218         
29219         Roo.each(this.files, function(file){
29220             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29221                 files.push(file);
29222                 return;
29223             }
29224
29225             o.target.remove();
29226
29227         }, this);
29228         
29229         this.files = files;
29230         
29231         this.refresh();
29232     },
29233     
29234     clear : function()
29235     {
29236         Roo.each(this.files, function(file){
29237             if(!file.target){
29238                 return;
29239             }
29240             
29241             file.target.remove();
29242
29243         }, this);
29244         
29245         this.files = [];
29246         
29247         this.refresh();
29248     },
29249     
29250     onClick : function(e, el, o)
29251     {
29252         e.preventDefault();
29253         
29254         this.fireEvent('click', this, o);
29255         
29256     },
29257     
29258     closable : function(closable)
29259     {
29260         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29261             
29262             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29263             
29264             if(closable){
29265                 el.show();
29266                 return;
29267             }
29268             
29269             el.hide();
29270             
29271         }, this);
29272     },
29273     
29274     xhrOnLoad : function(xhr)
29275     {
29276         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29277             el.remove();
29278         }, this);
29279         
29280         if (xhr.readyState !== 4) {
29281             this.arrange();
29282             this.fireEvent('exception', this, xhr);
29283             return;
29284         }
29285
29286         var response = Roo.decode(xhr.responseText);
29287         
29288         if(!response.success){
29289             this.arrange();
29290             this.fireEvent('exception', this, xhr);
29291             return;
29292         }
29293         
29294         var file = this.renderPreview(response.data);
29295         
29296         this.files.push(file);
29297         
29298         this.arrange();
29299         
29300         this.fireEvent('afterupload', this, xhr);
29301         
29302     },
29303     
29304     xhrOnError : function(xhr)
29305     {
29306         Roo.log('xhr on error');
29307         
29308         var response = Roo.decode(xhr.responseText);
29309           
29310         Roo.log(response);
29311         
29312         this.arrange();
29313     },
29314     
29315     process : function(file)
29316     {
29317         if(this.fireEvent('process', this, file) !== false){
29318             if(this.editable && file.type.indexOf('image') != -1){
29319                 this.fireEvent('edit', this, file);
29320                 return;
29321             }
29322
29323             this.uploadStart(file, false);
29324
29325             return;
29326         }
29327         
29328     },
29329     
29330     uploadStart : function(file, crop)
29331     {
29332         this.xhr = new XMLHttpRequest();
29333         
29334         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29335             this.arrange();
29336             return;
29337         }
29338         
29339         file.xhr = this.xhr;
29340             
29341         this.managerEl.createChild({
29342             tag : 'div',
29343             cls : 'roo-document-manager-loading',
29344             cn : [
29345                 {
29346                     tag : 'div',
29347                     tooltip : file.name,
29348                     cls : 'roo-document-manager-thumb',
29349                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29350                 }
29351             ]
29352
29353         });
29354
29355         this.xhr.open(this.method, this.url, true);
29356         
29357         var headers = {
29358             "Accept": "application/json",
29359             "Cache-Control": "no-cache",
29360             "X-Requested-With": "XMLHttpRequest"
29361         };
29362         
29363         for (var headerName in headers) {
29364             var headerValue = headers[headerName];
29365             if (headerValue) {
29366                 this.xhr.setRequestHeader(headerName, headerValue);
29367             }
29368         }
29369         
29370         var _this = this;
29371         
29372         this.xhr.onload = function()
29373         {
29374             _this.xhrOnLoad(_this.xhr);
29375         }
29376         
29377         this.xhr.onerror = function()
29378         {
29379             _this.xhrOnError(_this.xhr);
29380         }
29381         
29382         var formData = new FormData();
29383
29384         formData.append('returnHTML', 'NO');
29385         
29386         if(crop){
29387             formData.append('crop', crop);
29388         }
29389         
29390         formData.append(this.paramName, file, file.name);
29391         
29392         var options = {
29393             file : file, 
29394             manually : false
29395         };
29396         
29397         if(this.fireEvent('prepare', this, formData, options) != false){
29398             
29399             if(options.manually){
29400                 return;
29401             }
29402             
29403             this.xhr.send(formData);
29404             return;
29405         };
29406         
29407         this.uploadCancel();
29408     },
29409     
29410     uploadCancel : function()
29411     {
29412         if (this.xhr) {
29413             this.xhr.abort();
29414         }
29415         
29416         this.delegates = [];
29417         
29418         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29419             el.remove();
29420         }, this);
29421         
29422         this.arrange();
29423     },
29424     
29425     renderPreview : function(file)
29426     {
29427         if(typeof(file.target) != 'undefined' && file.target){
29428             return file;
29429         }
29430         
29431         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29432         
29433         var previewEl = this.managerEl.createChild({
29434             tag : 'div',
29435             cls : 'roo-document-manager-preview',
29436             cn : [
29437                 {
29438                     tag : 'div',
29439                     tooltip : file[this.toolTipName],
29440                     cls : 'roo-document-manager-thumb',
29441                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29442                 },
29443                 {
29444                     tag : 'button',
29445                     cls : 'close',
29446                     html : '<i class="fa fa-times-circle"></i>'
29447                 }
29448             ]
29449         });
29450
29451         var close = previewEl.select('button.close', true).first();
29452
29453         close.on('click', this.onRemove, this, file);
29454
29455         file.target = previewEl;
29456
29457         var image = previewEl.select('img', true).first();
29458         
29459         var _this = this;
29460         
29461         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29462         
29463         image.on('click', this.onClick, this, file);
29464         
29465         this.fireEvent('previewrendered', this, file);
29466         
29467         return file;
29468         
29469     },
29470     
29471     onPreviewLoad : function(file, image)
29472     {
29473         if(typeof(file.target) == 'undefined' || !file.target){
29474             return;
29475         }
29476         
29477         var width = image.dom.naturalWidth || image.dom.width;
29478         var height = image.dom.naturalHeight || image.dom.height;
29479         
29480         if(!this.previewResize) {
29481             return;
29482         }
29483         
29484         if(width > height){
29485             file.target.addClass('wide');
29486             return;
29487         }
29488         
29489         file.target.addClass('tall');
29490         return;
29491         
29492     },
29493     
29494     uploadFromSource : function(file, crop)
29495     {
29496         this.xhr = new XMLHttpRequest();
29497         
29498         this.managerEl.createChild({
29499             tag : 'div',
29500             cls : 'roo-document-manager-loading',
29501             cn : [
29502                 {
29503                     tag : 'div',
29504                     tooltip : file.name,
29505                     cls : 'roo-document-manager-thumb',
29506                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29507                 }
29508             ]
29509
29510         });
29511
29512         this.xhr.open(this.method, this.url, true);
29513         
29514         var headers = {
29515             "Accept": "application/json",
29516             "Cache-Control": "no-cache",
29517             "X-Requested-With": "XMLHttpRequest"
29518         };
29519         
29520         for (var headerName in headers) {
29521             var headerValue = headers[headerName];
29522             if (headerValue) {
29523                 this.xhr.setRequestHeader(headerName, headerValue);
29524             }
29525         }
29526         
29527         var _this = this;
29528         
29529         this.xhr.onload = function()
29530         {
29531             _this.xhrOnLoad(_this.xhr);
29532         }
29533         
29534         this.xhr.onerror = function()
29535         {
29536             _this.xhrOnError(_this.xhr);
29537         }
29538         
29539         var formData = new FormData();
29540
29541         formData.append('returnHTML', 'NO');
29542         
29543         formData.append('crop', crop);
29544         
29545         if(typeof(file.filename) != 'undefined'){
29546             formData.append('filename', file.filename);
29547         }
29548         
29549         if(typeof(file.mimetype) != 'undefined'){
29550             formData.append('mimetype', file.mimetype);
29551         }
29552         
29553         Roo.log(formData);
29554         
29555         if(this.fireEvent('prepare', this, formData) != false){
29556             this.xhr.send(formData);
29557         };
29558     }
29559 });
29560
29561 /*
29562 * Licence: LGPL
29563 */
29564
29565 /**
29566  * @class Roo.bootstrap.DocumentViewer
29567  * @extends Roo.bootstrap.Component
29568  * Bootstrap DocumentViewer class
29569  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29570  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29571  * 
29572  * @constructor
29573  * Create a new DocumentViewer
29574  * @param {Object} config The config object
29575  */
29576
29577 Roo.bootstrap.DocumentViewer = function(config){
29578     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29579     
29580     this.addEvents({
29581         /**
29582          * @event initial
29583          * Fire after initEvent
29584          * @param {Roo.bootstrap.DocumentViewer} this
29585          */
29586         "initial" : true,
29587         /**
29588          * @event click
29589          * Fire after click
29590          * @param {Roo.bootstrap.DocumentViewer} this
29591          */
29592         "click" : true,
29593         /**
29594          * @event download
29595          * Fire after download button
29596          * @param {Roo.bootstrap.DocumentViewer} this
29597          */
29598         "download" : true,
29599         /**
29600          * @event trash
29601          * Fire after trash button
29602          * @param {Roo.bootstrap.DocumentViewer} this
29603          */
29604         "trash" : true
29605         
29606     });
29607 };
29608
29609 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29610     
29611     showDownload : true,
29612     
29613     showTrash : true,
29614     
29615     getAutoCreate : function()
29616     {
29617         var cfg = {
29618             tag : 'div',
29619             cls : 'roo-document-viewer',
29620             cn : [
29621                 {
29622                     tag : 'div',
29623                     cls : 'roo-document-viewer-body',
29624                     cn : [
29625                         {
29626                             tag : 'div',
29627                             cls : 'roo-document-viewer-thumb',
29628                             cn : [
29629                                 {
29630                                     tag : 'img',
29631                                     cls : 'roo-document-viewer-image'
29632                                 }
29633                             ]
29634                         }
29635                     ]
29636                 },
29637                 {
29638                     tag : 'div',
29639                     cls : 'roo-document-viewer-footer',
29640                     cn : {
29641                         tag : 'div',
29642                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29643                         cn : [
29644                             {
29645                                 tag : 'div',
29646                                 cls : 'btn-group roo-document-viewer-download',
29647                                 cn : [
29648                                     {
29649                                         tag : 'button',
29650                                         cls : 'btn btn-default',
29651                                         html : '<i class="fa fa-download"></i>'
29652                                     }
29653                                 ]
29654                             },
29655                             {
29656                                 tag : 'div',
29657                                 cls : 'btn-group roo-document-viewer-trash',
29658                                 cn : [
29659                                     {
29660                                         tag : 'button',
29661                                         cls : 'btn btn-default',
29662                                         html : '<i class="fa fa-trash"></i>'
29663                                     }
29664                                 ]
29665                             }
29666                         ]
29667                     }
29668                 }
29669             ]
29670         };
29671         
29672         return cfg;
29673     },
29674     
29675     initEvents : function()
29676     {
29677         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29678         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29679         
29680         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29681         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29682         
29683         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29684         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29685         
29686         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29687         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29688         
29689         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29690         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29691         
29692         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29693         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29694         
29695         this.bodyEl.on('click', this.onClick, this);
29696         this.downloadBtn.on('click', this.onDownload, this);
29697         this.trashBtn.on('click', this.onTrash, this);
29698         
29699         this.downloadBtn.hide();
29700         this.trashBtn.hide();
29701         
29702         if(this.showDownload){
29703             this.downloadBtn.show();
29704         }
29705         
29706         if(this.showTrash){
29707             this.trashBtn.show();
29708         }
29709         
29710         if(!this.showDownload && !this.showTrash) {
29711             this.footerEl.hide();
29712         }
29713         
29714     },
29715     
29716     initial : function()
29717     {
29718         this.fireEvent('initial', this);
29719         
29720     },
29721     
29722     onClick : function(e)
29723     {
29724         e.preventDefault();
29725         
29726         this.fireEvent('click', this);
29727     },
29728     
29729     onDownload : function(e)
29730     {
29731         e.preventDefault();
29732         
29733         this.fireEvent('download', this);
29734     },
29735     
29736     onTrash : function(e)
29737     {
29738         e.preventDefault();
29739         
29740         this.fireEvent('trash', this);
29741     }
29742     
29743 });
29744 /*
29745  * - LGPL
29746  *
29747  * nav progress bar
29748  * 
29749  */
29750
29751 /**
29752  * @class Roo.bootstrap.NavProgressBar
29753  * @extends Roo.bootstrap.Component
29754  * Bootstrap NavProgressBar class
29755  * 
29756  * @constructor
29757  * Create a new nav progress bar
29758  * @param {Object} config The config object
29759  */
29760
29761 Roo.bootstrap.NavProgressBar = function(config){
29762     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29763
29764     this.bullets = this.bullets || [];
29765    
29766 //    Roo.bootstrap.NavProgressBar.register(this);
29767      this.addEvents({
29768         /**
29769              * @event changed
29770              * Fires when the active item changes
29771              * @param {Roo.bootstrap.NavProgressBar} this
29772              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29773              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29774          */
29775         'changed': true
29776      });
29777     
29778 };
29779
29780 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29781     
29782     bullets : [],
29783     barItems : [],
29784     
29785     getAutoCreate : function()
29786     {
29787         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29788         
29789         cfg = {
29790             tag : 'div',
29791             cls : 'roo-navigation-bar-group',
29792             cn : [
29793                 {
29794                     tag : 'div',
29795                     cls : 'roo-navigation-top-bar'
29796                 },
29797                 {
29798                     tag : 'div',
29799                     cls : 'roo-navigation-bullets-bar',
29800                     cn : [
29801                         {
29802                             tag : 'ul',
29803                             cls : 'roo-navigation-bar'
29804                         }
29805                     ]
29806                 },
29807                 
29808                 {
29809                     tag : 'div',
29810                     cls : 'roo-navigation-bottom-bar'
29811                 }
29812             ]
29813             
29814         };
29815         
29816         return cfg;
29817         
29818     },
29819     
29820     initEvents: function() 
29821     {
29822         
29823     },
29824     
29825     onRender : function(ct, position) 
29826     {
29827         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29828         
29829         if(this.bullets.length){
29830             Roo.each(this.bullets, function(b){
29831                this.addItem(b);
29832             }, this);
29833         }
29834         
29835         this.format();
29836         
29837     },
29838     
29839     addItem : function(cfg)
29840     {
29841         var item = new Roo.bootstrap.NavProgressItem(cfg);
29842         
29843         item.parentId = this.id;
29844         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29845         
29846         if(cfg.html){
29847             var top = new Roo.bootstrap.Element({
29848                 tag : 'div',
29849                 cls : 'roo-navigation-bar-text'
29850             });
29851             
29852             var bottom = new Roo.bootstrap.Element({
29853                 tag : 'div',
29854                 cls : 'roo-navigation-bar-text'
29855             });
29856             
29857             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29858             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29859             
29860             var topText = new Roo.bootstrap.Element({
29861                 tag : 'span',
29862                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29863             });
29864             
29865             var bottomText = new Roo.bootstrap.Element({
29866                 tag : 'span',
29867                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29868             });
29869             
29870             topText.onRender(top.el, null);
29871             bottomText.onRender(bottom.el, null);
29872             
29873             item.topEl = top;
29874             item.bottomEl = bottom;
29875         }
29876         
29877         this.barItems.push(item);
29878         
29879         return item;
29880     },
29881     
29882     getActive : function()
29883     {
29884         var active = false;
29885         
29886         Roo.each(this.barItems, function(v){
29887             
29888             if (!v.isActive()) {
29889                 return;
29890             }
29891             
29892             active = v;
29893             return false;
29894             
29895         });
29896         
29897         return active;
29898     },
29899     
29900     setActiveItem : function(item)
29901     {
29902         var prev = false;
29903         
29904         Roo.each(this.barItems, function(v){
29905             if (v.rid == item.rid) {
29906                 return ;
29907             }
29908             
29909             if (v.isActive()) {
29910                 v.setActive(false);
29911                 prev = v;
29912             }
29913         });
29914
29915         item.setActive(true);
29916         
29917         this.fireEvent('changed', this, item, prev);
29918     },
29919     
29920     getBarItem: function(rid)
29921     {
29922         var ret = false;
29923         
29924         Roo.each(this.barItems, function(e) {
29925             if (e.rid != rid) {
29926                 return;
29927             }
29928             
29929             ret =  e;
29930             return false;
29931         });
29932         
29933         return ret;
29934     },
29935     
29936     indexOfItem : function(item)
29937     {
29938         var index = false;
29939         
29940         Roo.each(this.barItems, function(v, i){
29941             
29942             if (v.rid != item.rid) {
29943                 return;
29944             }
29945             
29946             index = i;
29947             return false
29948         });
29949         
29950         return index;
29951     },
29952     
29953     setActiveNext : function()
29954     {
29955         var i = this.indexOfItem(this.getActive());
29956         
29957         if (i > this.barItems.length) {
29958             return;
29959         }
29960         
29961         this.setActiveItem(this.barItems[i+1]);
29962     },
29963     
29964     setActivePrev : function()
29965     {
29966         var i = this.indexOfItem(this.getActive());
29967         
29968         if (i  < 1) {
29969             return;
29970         }
29971         
29972         this.setActiveItem(this.barItems[i-1]);
29973     },
29974     
29975     format : function()
29976     {
29977         if(!this.barItems.length){
29978             return;
29979         }
29980      
29981         var width = 100 / this.barItems.length;
29982         
29983         Roo.each(this.barItems, function(i){
29984             i.el.setStyle('width', width + '%');
29985             i.topEl.el.setStyle('width', width + '%');
29986             i.bottomEl.el.setStyle('width', width + '%');
29987         }, this);
29988         
29989     }
29990     
29991 });
29992 /*
29993  * - LGPL
29994  *
29995  * Nav Progress Item
29996  * 
29997  */
29998
29999 /**
30000  * @class Roo.bootstrap.NavProgressItem
30001  * @extends Roo.bootstrap.Component
30002  * Bootstrap NavProgressItem class
30003  * @cfg {String} rid the reference id
30004  * @cfg {Boolean} active (true|false) Is item active default false
30005  * @cfg {Boolean} disabled (true|false) Is item active default false
30006  * @cfg {String} html
30007  * @cfg {String} position (top|bottom) text position default bottom
30008  * @cfg {String} icon show icon instead of number
30009  * 
30010  * @constructor
30011  * Create a new NavProgressItem
30012  * @param {Object} config The config object
30013  */
30014 Roo.bootstrap.NavProgressItem = function(config){
30015     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30016     this.addEvents({
30017         // raw events
30018         /**
30019          * @event click
30020          * The raw click event for the entire grid.
30021          * @param {Roo.bootstrap.NavProgressItem} this
30022          * @param {Roo.EventObject} e
30023          */
30024         "click" : true
30025     });
30026    
30027 };
30028
30029 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30030     
30031     rid : '',
30032     active : false,
30033     disabled : false,
30034     html : '',
30035     position : 'bottom',
30036     icon : false,
30037     
30038     getAutoCreate : function()
30039     {
30040         var iconCls = 'roo-navigation-bar-item-icon';
30041         
30042         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30043         
30044         var cfg = {
30045             tag: 'li',
30046             cls: 'roo-navigation-bar-item',
30047             cn : [
30048                 {
30049                     tag : 'i',
30050                     cls : iconCls
30051                 }
30052             ]
30053         };
30054         
30055         if(this.active){
30056             cfg.cls += ' active';
30057         }
30058         if(this.disabled){
30059             cfg.cls += ' disabled';
30060         }
30061         
30062         return cfg;
30063     },
30064     
30065     disable : function()
30066     {
30067         this.setDisabled(true);
30068     },
30069     
30070     enable : function()
30071     {
30072         this.setDisabled(false);
30073     },
30074     
30075     initEvents: function() 
30076     {
30077         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30078         
30079         this.iconEl.on('click', this.onClick, this);
30080     },
30081     
30082     onClick : function(e)
30083     {
30084         e.preventDefault();
30085         
30086         if(this.disabled){
30087             return;
30088         }
30089         
30090         if(this.fireEvent('click', this, e) === false){
30091             return;
30092         };
30093         
30094         this.parent().setActiveItem(this);
30095     },
30096     
30097     isActive: function () 
30098     {
30099         return this.active;
30100     },
30101     
30102     setActive : function(state)
30103     {
30104         if(this.active == state){
30105             return;
30106         }
30107         
30108         this.active = state;
30109         
30110         if (state) {
30111             this.el.addClass('active');
30112             return;
30113         }
30114         
30115         this.el.removeClass('active');
30116         
30117         return;
30118     },
30119     
30120     setDisabled : function(state)
30121     {
30122         if(this.disabled == state){
30123             return;
30124         }
30125         
30126         this.disabled = state;
30127         
30128         if (state) {
30129             this.el.addClass('disabled');
30130             return;
30131         }
30132         
30133         this.el.removeClass('disabled');
30134     },
30135     
30136     tooltipEl : function()
30137     {
30138         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30139     }
30140 });
30141  
30142
30143  /*
30144  * - LGPL
30145  *
30146  * FieldLabel
30147  * 
30148  */
30149
30150 /**
30151  * @class Roo.bootstrap.FieldLabel
30152  * @extends Roo.bootstrap.Component
30153  * Bootstrap FieldLabel class
30154  * @cfg {String} html contents of the element
30155  * @cfg {String} tag tag of the element default label
30156  * @cfg {String} cls class of the element
30157  * @cfg {String} target label target 
30158  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30159  * @cfg {String} invalidClass default "text-warning"
30160  * @cfg {String} validClass default "text-success"
30161  * @cfg {String} iconTooltip default "This field is required"
30162  * @cfg {String} indicatorpos (left|right) default left
30163  * 
30164  * @constructor
30165  * Create a new FieldLabel
30166  * @param {Object} config The config object
30167  */
30168
30169 Roo.bootstrap.FieldLabel = function(config){
30170     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30171     
30172     this.addEvents({
30173             /**
30174              * @event invalid
30175              * Fires after the field has been marked as invalid.
30176              * @param {Roo.form.FieldLabel} this
30177              * @param {String} msg The validation message
30178              */
30179             invalid : true,
30180             /**
30181              * @event valid
30182              * Fires after the field has been validated with no errors.
30183              * @param {Roo.form.FieldLabel} this
30184              */
30185             valid : true
30186         });
30187 };
30188
30189 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30190     
30191     tag: 'label',
30192     cls: '',
30193     html: '',
30194     target: '',
30195     allowBlank : true,
30196     invalidClass : 'has-warning',
30197     validClass : 'has-success',
30198     iconTooltip : 'This field is required',
30199     indicatorpos : 'left',
30200     
30201     getAutoCreate : function(){
30202         
30203         var cls = "";
30204         if (!this.allowBlank) {
30205             cls  = "visible";
30206         }
30207         
30208         var cfg = {
30209             tag : this.tag,
30210             cls : 'roo-bootstrap-field-label ' + this.cls,
30211             for : this.target,
30212             cn : [
30213                 {
30214                     tag : 'i',
30215                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30216                     tooltip : this.iconTooltip
30217                 },
30218                 {
30219                     tag : 'span',
30220                     html : this.html
30221                 }
30222             ] 
30223         };
30224         
30225         if(this.indicatorpos == 'right'){
30226             var cfg = {
30227                 tag : this.tag,
30228                 cls : 'roo-bootstrap-field-label ' + this.cls,
30229                 for : this.target,
30230                 cn : [
30231                     {
30232                         tag : 'span',
30233                         html : this.html
30234                     },
30235                     {
30236                         tag : 'i',
30237                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30238                         tooltip : this.iconTooltip
30239                     }
30240                 ] 
30241             };
30242         }
30243         
30244         return cfg;
30245     },
30246     
30247     initEvents: function() 
30248     {
30249         Roo.bootstrap.Element.superclass.initEvents.call(this);
30250         
30251         this.indicator = this.indicatorEl();
30252         
30253         if(this.indicator){
30254             this.indicator.removeClass('visible');
30255             this.indicator.addClass('invisible');
30256         }
30257         
30258         Roo.bootstrap.FieldLabel.register(this);
30259     },
30260     
30261     indicatorEl : function()
30262     {
30263         var indicator = this.el.select('i.roo-required-indicator',true).first();
30264         
30265         if(!indicator){
30266             return false;
30267         }
30268         
30269         return indicator;
30270         
30271     },
30272     
30273     /**
30274      * Mark this field as valid
30275      */
30276     markValid : function()
30277     {
30278         if(this.indicator){
30279             this.indicator.removeClass('visible');
30280             this.indicator.addClass('invisible');
30281         }
30282         
30283         this.el.removeClass(this.invalidClass);
30284         
30285         this.el.addClass(this.validClass);
30286         
30287         this.fireEvent('valid', this);
30288     },
30289     
30290     /**
30291      * Mark this field as invalid
30292      * @param {String} msg The validation message
30293      */
30294     markInvalid : function(msg)
30295     {
30296         if(this.indicator){
30297             this.indicator.removeClass('invisible');
30298             this.indicator.addClass('visible');
30299         }
30300         
30301         this.el.removeClass(this.validClass);
30302         
30303         this.el.addClass(this.invalidClass);
30304         
30305         this.fireEvent('invalid', this, msg);
30306     }
30307     
30308    
30309 });
30310
30311 Roo.apply(Roo.bootstrap.FieldLabel, {
30312     
30313     groups: {},
30314     
30315      /**
30316     * register a FieldLabel Group
30317     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30318     */
30319     register : function(label)
30320     {
30321         if(this.groups.hasOwnProperty(label.target)){
30322             return;
30323         }
30324      
30325         this.groups[label.target] = label;
30326         
30327     },
30328     /**
30329     * fetch a FieldLabel Group based on the target
30330     * @param {string} target
30331     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30332     */
30333     get: function(target) {
30334         if (typeof(this.groups[target]) == 'undefined') {
30335             return false;
30336         }
30337         
30338         return this.groups[target] ;
30339     }
30340 });
30341
30342  
30343
30344  /*
30345  * - LGPL
30346  *
30347  * page DateSplitField.
30348  * 
30349  */
30350
30351
30352 /**
30353  * @class Roo.bootstrap.DateSplitField
30354  * @extends Roo.bootstrap.Component
30355  * Bootstrap DateSplitField class
30356  * @cfg {string} fieldLabel - the label associated
30357  * @cfg {Number} labelWidth set the width of label (0-12)
30358  * @cfg {String} labelAlign (top|left)
30359  * @cfg {Boolean} dayAllowBlank (true|false) default false
30360  * @cfg {Boolean} monthAllowBlank (true|false) default false
30361  * @cfg {Boolean} yearAllowBlank (true|false) default false
30362  * @cfg {string} dayPlaceholder 
30363  * @cfg {string} monthPlaceholder
30364  * @cfg {string} yearPlaceholder
30365  * @cfg {string} dayFormat default 'd'
30366  * @cfg {string} monthFormat default 'm'
30367  * @cfg {string} yearFormat default 'Y'
30368  * @cfg {Number} labellg set the width of label (1-12)
30369  * @cfg {Number} labelmd set the width of label (1-12)
30370  * @cfg {Number} labelsm set the width of label (1-12)
30371  * @cfg {Number} labelxs set the width of label (1-12)
30372
30373  *     
30374  * @constructor
30375  * Create a new DateSplitField
30376  * @param {Object} config The config object
30377  */
30378
30379 Roo.bootstrap.DateSplitField = function(config){
30380     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30381     
30382     this.addEvents({
30383         // raw events
30384          /**
30385          * @event years
30386          * getting the data of years
30387          * @param {Roo.bootstrap.DateSplitField} this
30388          * @param {Object} years
30389          */
30390         "years" : true,
30391         /**
30392          * @event days
30393          * getting the data of days
30394          * @param {Roo.bootstrap.DateSplitField} this
30395          * @param {Object} days
30396          */
30397         "days" : true,
30398         /**
30399          * @event invalid
30400          * Fires after the field has been marked as invalid.
30401          * @param {Roo.form.Field} this
30402          * @param {String} msg The validation message
30403          */
30404         invalid : true,
30405        /**
30406          * @event valid
30407          * Fires after the field has been validated with no errors.
30408          * @param {Roo.form.Field} this
30409          */
30410         valid : true
30411     });
30412 };
30413
30414 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30415     
30416     fieldLabel : '',
30417     labelAlign : 'top',
30418     labelWidth : 3,
30419     dayAllowBlank : false,
30420     monthAllowBlank : false,
30421     yearAllowBlank : false,
30422     dayPlaceholder : '',
30423     monthPlaceholder : '',
30424     yearPlaceholder : '',
30425     dayFormat : 'd',
30426     monthFormat : 'm',
30427     yearFormat : 'Y',
30428     isFormField : true,
30429     labellg : 0,
30430     labelmd : 0,
30431     labelsm : 0,
30432     labelxs : 0,
30433     
30434     getAutoCreate : function()
30435     {
30436         var cfg = {
30437             tag : 'div',
30438             cls : 'row roo-date-split-field-group',
30439             cn : [
30440                 {
30441                     tag : 'input',
30442                     type : 'hidden',
30443                     cls : 'form-hidden-field roo-date-split-field-group-value',
30444                     name : this.name
30445                 }
30446             ]
30447         };
30448         
30449         var labelCls = 'col-md-12';
30450         var contentCls = 'col-md-4';
30451         
30452         if(this.fieldLabel){
30453             
30454             var label = {
30455                 tag : 'div',
30456                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30457                 cn : [
30458                     {
30459                         tag : 'label',
30460                         html : this.fieldLabel
30461                     }
30462                 ]
30463             };
30464             
30465             if(this.labelAlign == 'left'){
30466             
30467                 if(this.labelWidth > 12){
30468                     label.style = "width: " + this.labelWidth + 'px';
30469                 }
30470
30471                 if(this.labelWidth < 13 && this.labelmd == 0){
30472                     this.labelmd = this.labelWidth;
30473                 }
30474
30475                 if(this.labellg > 0){
30476                     labelCls = ' col-lg-' + this.labellg;
30477                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30478                 }
30479
30480                 if(this.labelmd > 0){
30481                     labelCls = ' col-md-' + this.labelmd;
30482                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30483                 }
30484
30485                 if(this.labelsm > 0){
30486                     labelCls = ' col-sm-' + this.labelsm;
30487                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30488                 }
30489
30490                 if(this.labelxs > 0){
30491                     labelCls = ' col-xs-' + this.labelxs;
30492                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30493                 }
30494             }
30495             
30496             label.cls += ' ' + labelCls;
30497             
30498             cfg.cn.push(label);
30499         }
30500         
30501         Roo.each(['day', 'month', 'year'], function(t){
30502             cfg.cn.push({
30503                 tag : 'div',
30504                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30505             });
30506         }, this);
30507         
30508         return cfg;
30509     },
30510     
30511     inputEl: function ()
30512     {
30513         return this.el.select('.roo-date-split-field-group-value', true).first();
30514     },
30515     
30516     onRender : function(ct, position) 
30517     {
30518         var _this = this;
30519         
30520         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30521         
30522         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30523         
30524         this.dayField = new Roo.bootstrap.ComboBox({
30525             allowBlank : this.dayAllowBlank,
30526             alwaysQuery : true,
30527             displayField : 'value',
30528             editable : false,
30529             fieldLabel : '',
30530             forceSelection : true,
30531             mode : 'local',
30532             placeholder : this.dayPlaceholder,
30533             selectOnFocus : true,
30534             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30535             triggerAction : 'all',
30536             typeAhead : true,
30537             valueField : 'value',
30538             store : new Roo.data.SimpleStore({
30539                 data : (function() {    
30540                     var days = [];
30541                     _this.fireEvent('days', _this, days);
30542                     return days;
30543                 })(),
30544                 fields : [ 'value' ]
30545             }),
30546             listeners : {
30547                 select : function (_self, record, index)
30548                 {
30549                     _this.setValue(_this.getValue());
30550                 }
30551             }
30552         });
30553
30554         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30555         
30556         this.monthField = new Roo.bootstrap.MonthField({
30557             after : '<i class=\"fa fa-calendar\"></i>',
30558             allowBlank : this.monthAllowBlank,
30559             placeholder : this.monthPlaceholder,
30560             readOnly : true,
30561             listeners : {
30562                 render : function (_self)
30563                 {
30564                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30565                         e.preventDefault();
30566                         _self.focus();
30567                     });
30568                 },
30569                 select : function (_self, oldvalue, newvalue)
30570                 {
30571                     _this.setValue(_this.getValue());
30572                 }
30573             }
30574         });
30575         
30576         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30577         
30578         this.yearField = new Roo.bootstrap.ComboBox({
30579             allowBlank : this.yearAllowBlank,
30580             alwaysQuery : true,
30581             displayField : 'value',
30582             editable : false,
30583             fieldLabel : '',
30584             forceSelection : true,
30585             mode : 'local',
30586             placeholder : this.yearPlaceholder,
30587             selectOnFocus : true,
30588             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30589             triggerAction : 'all',
30590             typeAhead : true,
30591             valueField : 'value',
30592             store : new Roo.data.SimpleStore({
30593                 data : (function() {
30594                     var years = [];
30595                     _this.fireEvent('years', _this, years);
30596                     return years;
30597                 })(),
30598                 fields : [ 'value' ]
30599             }),
30600             listeners : {
30601                 select : function (_self, record, index)
30602                 {
30603                     _this.setValue(_this.getValue());
30604                 }
30605             }
30606         });
30607
30608         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30609     },
30610     
30611     setValue : function(v, format)
30612     {
30613         this.inputEl.dom.value = v;
30614         
30615         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30616         
30617         var d = Date.parseDate(v, f);
30618         
30619         if(!d){
30620             this.validate();
30621             return;
30622         }
30623         
30624         this.setDay(d.format(this.dayFormat));
30625         this.setMonth(d.format(this.monthFormat));
30626         this.setYear(d.format(this.yearFormat));
30627         
30628         this.validate();
30629         
30630         return;
30631     },
30632     
30633     setDay : function(v)
30634     {
30635         this.dayField.setValue(v);
30636         this.inputEl.dom.value = this.getValue();
30637         this.validate();
30638         return;
30639     },
30640     
30641     setMonth : function(v)
30642     {
30643         this.monthField.setValue(v, true);
30644         this.inputEl.dom.value = this.getValue();
30645         this.validate();
30646         return;
30647     },
30648     
30649     setYear : function(v)
30650     {
30651         this.yearField.setValue(v);
30652         this.inputEl.dom.value = this.getValue();
30653         this.validate();
30654         return;
30655     },
30656     
30657     getDay : function()
30658     {
30659         return this.dayField.getValue();
30660     },
30661     
30662     getMonth : function()
30663     {
30664         return this.monthField.getValue();
30665     },
30666     
30667     getYear : function()
30668     {
30669         return this.yearField.getValue();
30670     },
30671     
30672     getValue : function()
30673     {
30674         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30675         
30676         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30677         
30678         return date;
30679     },
30680     
30681     reset : function()
30682     {
30683         this.setDay('');
30684         this.setMonth('');
30685         this.setYear('');
30686         this.inputEl.dom.value = '';
30687         this.validate();
30688         return;
30689     },
30690     
30691     validate : function()
30692     {
30693         var d = this.dayField.validate();
30694         var m = this.monthField.validate();
30695         var y = this.yearField.validate();
30696         
30697         var valid = true;
30698         
30699         if(
30700                 (!this.dayAllowBlank && !d) ||
30701                 (!this.monthAllowBlank && !m) ||
30702                 (!this.yearAllowBlank && !y)
30703         ){
30704             valid = false;
30705         }
30706         
30707         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30708             return valid;
30709         }
30710         
30711         if(valid){
30712             this.markValid();
30713             return valid;
30714         }
30715         
30716         this.markInvalid();
30717         
30718         return valid;
30719     },
30720     
30721     markValid : function()
30722     {
30723         
30724         var label = this.el.select('label', true).first();
30725         var icon = this.el.select('i.fa-star', true).first();
30726
30727         if(label && icon){
30728             icon.remove();
30729         }
30730         
30731         this.fireEvent('valid', this);
30732     },
30733     
30734      /**
30735      * Mark this field as invalid
30736      * @param {String} msg The validation message
30737      */
30738     markInvalid : function(msg)
30739     {
30740         
30741         var label = this.el.select('label', true).first();
30742         var icon = this.el.select('i.fa-star', true).first();
30743
30744         if(label && !icon){
30745             this.el.select('.roo-date-split-field-label', true).createChild({
30746                 tag : 'i',
30747                 cls : 'text-danger fa fa-lg fa-star',
30748                 tooltip : 'This field is required',
30749                 style : 'margin-right:5px;'
30750             }, label, true);
30751         }
30752         
30753         this.fireEvent('invalid', this, msg);
30754     },
30755     
30756     clearInvalid : function()
30757     {
30758         var label = this.el.select('label', true).first();
30759         var icon = this.el.select('i.fa-star', true).first();
30760
30761         if(label && icon){
30762             icon.remove();
30763         }
30764         
30765         this.fireEvent('valid', this);
30766     },
30767     
30768     getName: function()
30769     {
30770         return this.name;
30771     }
30772     
30773 });
30774
30775  /**
30776  *
30777  * This is based on 
30778  * http://masonry.desandro.com
30779  *
30780  * The idea is to render all the bricks based on vertical width...
30781  *
30782  * The original code extends 'outlayer' - we might need to use that....
30783  * 
30784  */
30785
30786
30787 /**
30788  * @class Roo.bootstrap.LayoutMasonry
30789  * @extends Roo.bootstrap.Component
30790  * Bootstrap Layout Masonry class
30791  * 
30792  * @constructor
30793  * Create a new Element
30794  * @param {Object} config The config object
30795  */
30796
30797 Roo.bootstrap.LayoutMasonry = function(config){
30798     
30799     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30800     
30801     this.bricks = [];
30802     
30803     Roo.bootstrap.LayoutMasonry.register(this);
30804     
30805     this.addEvents({
30806         // raw events
30807         /**
30808          * @event layout
30809          * Fire after layout the items
30810          * @param {Roo.bootstrap.LayoutMasonry} this
30811          * @param {Roo.EventObject} e
30812          */
30813         "layout" : true
30814     });
30815     
30816 };
30817
30818 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30819     
30820     /**
30821      * @cfg {Boolean} isLayoutInstant = no animation?
30822      */   
30823     isLayoutInstant : false, // needed?
30824    
30825     /**
30826      * @cfg {Number} boxWidth  width of the columns
30827      */   
30828     boxWidth : 450,
30829     
30830       /**
30831      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30832      */   
30833     boxHeight : 0,
30834     
30835     /**
30836      * @cfg {Number} padWidth padding below box..
30837      */   
30838     padWidth : 10, 
30839     
30840     /**
30841      * @cfg {Number} gutter gutter width..
30842      */   
30843     gutter : 10,
30844     
30845      /**
30846      * @cfg {Number} maxCols maximum number of columns
30847      */   
30848     
30849     maxCols: 0,
30850     
30851     /**
30852      * @cfg {Boolean} isAutoInitial defalut true
30853      */   
30854     isAutoInitial : true, 
30855     
30856     containerWidth: 0,
30857     
30858     /**
30859      * @cfg {Boolean} isHorizontal defalut false
30860      */   
30861     isHorizontal : false, 
30862
30863     currentSize : null,
30864     
30865     tag: 'div',
30866     
30867     cls: '',
30868     
30869     bricks: null, //CompositeElement
30870     
30871     cols : 1,
30872     
30873     _isLayoutInited : false,
30874     
30875 //    isAlternative : false, // only use for vertical layout...
30876     
30877     /**
30878      * @cfg {Number} alternativePadWidth padding below box..
30879      */   
30880     alternativePadWidth : 50,
30881     
30882     selectedBrick : [],
30883     
30884     getAutoCreate : function(){
30885         
30886         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30887         
30888         var cfg = {
30889             tag: this.tag,
30890             cls: 'blog-masonary-wrapper ' + this.cls,
30891             cn : {
30892                 cls : 'mas-boxes masonary'
30893             }
30894         };
30895         
30896         return cfg;
30897     },
30898     
30899     getChildContainer: function( )
30900     {
30901         if (this.boxesEl) {
30902             return this.boxesEl;
30903         }
30904         
30905         this.boxesEl = this.el.select('.mas-boxes').first();
30906         
30907         return this.boxesEl;
30908     },
30909     
30910     
30911     initEvents : function()
30912     {
30913         var _this = this;
30914         
30915         if(this.isAutoInitial){
30916             Roo.log('hook children rendered');
30917             this.on('childrenrendered', function() {
30918                 Roo.log('children rendered');
30919                 _this.initial();
30920             } ,this);
30921         }
30922     },
30923     
30924     initial : function()
30925     {
30926         this.selectedBrick = [];
30927         
30928         this.currentSize = this.el.getBox(true);
30929         
30930         Roo.EventManager.onWindowResize(this.resize, this); 
30931
30932         if(!this.isAutoInitial){
30933             this.layout();
30934             return;
30935         }
30936         
30937         this.layout();
30938         
30939         return;
30940         //this.layout.defer(500,this);
30941         
30942     },
30943     
30944     resize : function()
30945     {
30946         var cs = this.el.getBox(true);
30947         
30948         if (
30949                 this.currentSize.width == cs.width && 
30950                 this.currentSize.x == cs.x && 
30951                 this.currentSize.height == cs.height && 
30952                 this.currentSize.y == cs.y 
30953         ) {
30954             Roo.log("no change in with or X or Y");
30955             return;
30956         }
30957         
30958         this.currentSize = cs;
30959         
30960         this.layout();
30961         
30962     },
30963     
30964     layout : function()
30965     {   
30966         this._resetLayout();
30967         
30968         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30969         
30970         this.layoutItems( isInstant );
30971       
30972         this._isLayoutInited = true;
30973         
30974         this.fireEvent('layout', this);
30975         
30976     },
30977     
30978     _resetLayout : function()
30979     {
30980         if(this.isHorizontal){
30981             this.horizontalMeasureColumns();
30982             return;
30983         }
30984         
30985         this.verticalMeasureColumns();
30986         
30987     },
30988     
30989     verticalMeasureColumns : function()
30990     {
30991         this.getContainerWidth();
30992         
30993 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30994 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30995 //            return;
30996 //        }
30997         
30998         var boxWidth = this.boxWidth + this.padWidth;
30999         
31000         if(this.containerWidth < this.boxWidth){
31001             boxWidth = this.containerWidth
31002         }
31003         
31004         var containerWidth = this.containerWidth;
31005         
31006         var cols = Math.floor(containerWidth / boxWidth);
31007         
31008         this.cols = Math.max( cols, 1 );
31009         
31010         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31011         
31012         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31013         
31014         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31015         
31016         this.colWidth = boxWidth + avail - this.padWidth;
31017         
31018         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31019         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31020     },
31021     
31022     horizontalMeasureColumns : function()
31023     {
31024         this.getContainerWidth();
31025         
31026         var boxWidth = this.boxWidth;
31027         
31028         if(this.containerWidth < boxWidth){
31029             boxWidth = this.containerWidth;
31030         }
31031         
31032         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31033         
31034         this.el.setHeight(boxWidth);
31035         
31036     },
31037     
31038     getContainerWidth : function()
31039     {
31040         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31041     },
31042     
31043     layoutItems : function( isInstant )
31044     {
31045         Roo.log(this.bricks);
31046         
31047         var items = Roo.apply([], this.bricks);
31048         
31049         if(this.isHorizontal){
31050             this._horizontalLayoutItems( items , isInstant );
31051             return;
31052         }
31053         
31054 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31055 //            this._verticalAlternativeLayoutItems( items , isInstant );
31056 //            return;
31057 //        }
31058         
31059         this._verticalLayoutItems( items , isInstant );
31060         
31061     },
31062     
31063     _verticalLayoutItems : function ( items , isInstant)
31064     {
31065         if ( !items || !items.length ) {
31066             return;
31067         }
31068         
31069         var standard = [
31070             ['xs', 'xs', 'xs', 'tall'],
31071             ['xs', 'xs', 'tall'],
31072             ['xs', 'xs', 'sm'],
31073             ['xs', 'xs', 'xs'],
31074             ['xs', 'tall'],
31075             ['xs', 'sm'],
31076             ['xs', 'xs'],
31077             ['xs'],
31078             
31079             ['sm', 'xs', 'xs'],
31080             ['sm', 'xs'],
31081             ['sm'],
31082             
31083             ['tall', 'xs', 'xs', 'xs'],
31084             ['tall', 'xs', 'xs'],
31085             ['tall', 'xs'],
31086             ['tall']
31087             
31088         ];
31089         
31090         var queue = [];
31091         
31092         var boxes = [];
31093         
31094         var box = [];
31095         
31096         Roo.each(items, function(item, k){
31097             
31098             switch (item.size) {
31099                 // these layouts take up a full box,
31100                 case 'md' :
31101                 case 'md-left' :
31102                 case 'md-right' :
31103                 case 'wide' :
31104                     
31105                     if(box.length){
31106                         boxes.push(box);
31107                         box = [];
31108                     }
31109                     
31110                     boxes.push([item]);
31111                     
31112                     break;
31113                     
31114                 case 'xs' :
31115                 case 'sm' :
31116                 case 'tall' :
31117                     
31118                     box.push(item);
31119                     
31120                     break;
31121                 default :
31122                     break;
31123                     
31124             }
31125             
31126         }, this);
31127         
31128         if(box.length){
31129             boxes.push(box);
31130             box = [];
31131         }
31132         
31133         var filterPattern = function(box, length)
31134         {
31135             if(!box.length){
31136                 return;
31137             }
31138             
31139             var match = false;
31140             
31141             var pattern = box.slice(0, length);
31142             
31143             var format = [];
31144             
31145             Roo.each(pattern, function(i){
31146                 format.push(i.size);
31147             }, this);
31148             
31149             Roo.each(standard, function(s){
31150                 
31151                 if(String(s) != String(format)){
31152                     return;
31153                 }
31154                 
31155                 match = true;
31156                 return false;
31157                 
31158             }, this);
31159             
31160             if(!match && length == 1){
31161                 return;
31162             }
31163             
31164             if(!match){
31165                 filterPattern(box, length - 1);
31166                 return;
31167             }
31168                 
31169             queue.push(pattern);
31170
31171             box = box.slice(length, box.length);
31172
31173             filterPattern(box, 4);
31174
31175             return;
31176             
31177         }
31178         
31179         Roo.each(boxes, function(box, k){
31180             
31181             if(!box.length){
31182                 return;
31183             }
31184             
31185             if(box.length == 1){
31186                 queue.push(box);
31187                 return;
31188             }
31189             
31190             filterPattern(box, 4);
31191             
31192         }, this);
31193         
31194         this._processVerticalLayoutQueue( queue, isInstant );
31195         
31196     },
31197     
31198 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31199 //    {
31200 //        if ( !items || !items.length ) {
31201 //            return;
31202 //        }
31203 //
31204 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31205 //        
31206 //    },
31207     
31208     _horizontalLayoutItems : function ( items , isInstant)
31209     {
31210         if ( !items || !items.length || items.length < 3) {
31211             return;
31212         }
31213         
31214         items.reverse();
31215         
31216         var eItems = items.slice(0, 3);
31217         
31218         items = items.slice(3, items.length);
31219         
31220         var standard = [
31221             ['xs', 'xs', 'xs', 'wide'],
31222             ['xs', 'xs', 'wide'],
31223             ['xs', 'xs', 'sm'],
31224             ['xs', 'xs', 'xs'],
31225             ['xs', 'wide'],
31226             ['xs', 'sm'],
31227             ['xs', 'xs'],
31228             ['xs'],
31229             
31230             ['sm', 'xs', 'xs'],
31231             ['sm', 'xs'],
31232             ['sm'],
31233             
31234             ['wide', 'xs', 'xs', 'xs'],
31235             ['wide', 'xs', 'xs'],
31236             ['wide', 'xs'],
31237             ['wide'],
31238             
31239             ['wide-thin']
31240         ];
31241         
31242         var queue = [];
31243         
31244         var boxes = [];
31245         
31246         var box = [];
31247         
31248         Roo.each(items, function(item, k){
31249             
31250             switch (item.size) {
31251                 case 'md' :
31252                 case 'md-left' :
31253                 case 'md-right' :
31254                 case 'tall' :
31255                     
31256                     if(box.length){
31257                         boxes.push(box);
31258                         box = [];
31259                     }
31260                     
31261                     boxes.push([item]);
31262                     
31263                     break;
31264                     
31265                 case 'xs' :
31266                 case 'sm' :
31267                 case 'wide' :
31268                 case 'wide-thin' :
31269                     
31270                     box.push(item);
31271                     
31272                     break;
31273                 default :
31274                     break;
31275                     
31276             }
31277             
31278         }, this);
31279         
31280         if(box.length){
31281             boxes.push(box);
31282             box = [];
31283         }
31284         
31285         var filterPattern = function(box, length)
31286         {
31287             if(!box.length){
31288                 return;
31289             }
31290             
31291             var match = false;
31292             
31293             var pattern = box.slice(0, length);
31294             
31295             var format = [];
31296             
31297             Roo.each(pattern, function(i){
31298                 format.push(i.size);
31299             }, this);
31300             
31301             Roo.each(standard, function(s){
31302                 
31303                 if(String(s) != String(format)){
31304                     return;
31305                 }
31306                 
31307                 match = true;
31308                 return false;
31309                 
31310             }, this);
31311             
31312             if(!match && length == 1){
31313                 return;
31314             }
31315             
31316             if(!match){
31317                 filterPattern(box, length - 1);
31318                 return;
31319             }
31320                 
31321             queue.push(pattern);
31322
31323             box = box.slice(length, box.length);
31324
31325             filterPattern(box, 4);
31326
31327             return;
31328             
31329         }
31330         
31331         Roo.each(boxes, function(box, k){
31332             
31333             if(!box.length){
31334                 return;
31335             }
31336             
31337             if(box.length == 1){
31338                 queue.push(box);
31339                 return;
31340             }
31341             
31342             filterPattern(box, 4);
31343             
31344         }, this);
31345         
31346         
31347         var prune = [];
31348         
31349         var pos = this.el.getBox(true);
31350         
31351         var minX = pos.x;
31352         
31353         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31354         
31355         var hit_end = false;
31356         
31357         Roo.each(queue, function(box){
31358             
31359             if(hit_end){
31360                 
31361                 Roo.each(box, function(b){
31362                 
31363                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31364                     b.el.hide();
31365
31366                 }, this);
31367
31368                 return;
31369             }
31370             
31371             var mx = 0;
31372             
31373             Roo.each(box, function(b){
31374                 
31375                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31376                 b.el.show();
31377
31378                 mx = Math.max(mx, b.x);
31379                 
31380             }, this);
31381             
31382             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31383             
31384             if(maxX < minX){
31385                 
31386                 Roo.each(box, function(b){
31387                 
31388                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31389                     b.el.hide();
31390                     
31391                 }, this);
31392                 
31393                 hit_end = true;
31394                 
31395                 return;
31396             }
31397             
31398             prune.push(box);
31399             
31400         }, this);
31401         
31402         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31403     },
31404     
31405     /** Sets position of item in DOM
31406     * @param {Element} item
31407     * @param {Number} x - horizontal position
31408     * @param {Number} y - vertical position
31409     * @param {Boolean} isInstant - disables transitions
31410     */
31411     _processVerticalLayoutQueue : function( queue, isInstant )
31412     {
31413         var pos = this.el.getBox(true);
31414         var x = pos.x;
31415         var y = pos.y;
31416         var maxY = [];
31417         
31418         for (var i = 0; i < this.cols; i++){
31419             maxY[i] = pos.y;
31420         }
31421         
31422         Roo.each(queue, function(box, k){
31423             
31424             var col = k % this.cols;
31425             
31426             Roo.each(box, function(b,kk){
31427                 
31428                 b.el.position('absolute');
31429                 
31430                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31431                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31432                 
31433                 if(b.size == 'md-left' || b.size == 'md-right'){
31434                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31435                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31436                 }
31437                 
31438                 b.el.setWidth(width);
31439                 b.el.setHeight(height);
31440                 // iframe?
31441                 b.el.select('iframe',true).setSize(width,height);
31442                 
31443             }, this);
31444             
31445             for (var i = 0; i < this.cols; i++){
31446                 
31447                 if(maxY[i] < maxY[col]){
31448                     col = i;
31449                     continue;
31450                 }
31451                 
31452                 col = Math.min(col, i);
31453                 
31454             }
31455             
31456             x = pos.x + col * (this.colWidth + this.padWidth);
31457             
31458             y = maxY[col];
31459             
31460             var positions = [];
31461             
31462             switch (box.length){
31463                 case 1 :
31464                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31465                     break;
31466                 case 2 :
31467                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31468                     break;
31469                 case 3 :
31470                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31471                     break;
31472                 case 4 :
31473                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31474                     break;
31475                 default :
31476                     break;
31477             }
31478             
31479             Roo.each(box, function(b,kk){
31480                 
31481                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31482                 
31483                 var sz = b.el.getSize();
31484                 
31485                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31486                 
31487             }, this);
31488             
31489         }, this);
31490         
31491         var mY = 0;
31492         
31493         for (var i = 0; i < this.cols; i++){
31494             mY = Math.max(mY, maxY[i]);
31495         }
31496         
31497         this.el.setHeight(mY - pos.y);
31498         
31499     },
31500     
31501 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31502 //    {
31503 //        var pos = this.el.getBox(true);
31504 //        var x = pos.x;
31505 //        var y = pos.y;
31506 //        var maxX = pos.right;
31507 //        
31508 //        var maxHeight = 0;
31509 //        
31510 //        Roo.each(items, function(item, k){
31511 //            
31512 //            var c = k % 2;
31513 //            
31514 //            item.el.position('absolute');
31515 //                
31516 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31517 //
31518 //            item.el.setWidth(width);
31519 //
31520 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31521 //
31522 //            item.el.setHeight(height);
31523 //            
31524 //            if(c == 0){
31525 //                item.el.setXY([x, y], isInstant ? false : true);
31526 //            } else {
31527 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31528 //            }
31529 //            
31530 //            y = y + height + this.alternativePadWidth;
31531 //            
31532 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31533 //            
31534 //        }, this);
31535 //        
31536 //        this.el.setHeight(maxHeight);
31537 //        
31538 //    },
31539     
31540     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31541     {
31542         var pos = this.el.getBox(true);
31543         
31544         var minX = pos.x;
31545         var minY = pos.y;
31546         
31547         var maxX = pos.right;
31548         
31549         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31550         
31551         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31552         
31553         Roo.each(queue, function(box, k){
31554             
31555             Roo.each(box, function(b, kk){
31556                 
31557                 b.el.position('absolute');
31558                 
31559                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31560                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31561                 
31562                 if(b.size == 'md-left' || b.size == 'md-right'){
31563                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31564                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31565                 }
31566                 
31567                 b.el.setWidth(width);
31568                 b.el.setHeight(height);
31569                 
31570             }, this);
31571             
31572             if(!box.length){
31573                 return;
31574             }
31575             
31576             var positions = [];
31577             
31578             switch (box.length){
31579                 case 1 :
31580                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31581                     break;
31582                 case 2 :
31583                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31584                     break;
31585                 case 3 :
31586                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31587                     break;
31588                 case 4 :
31589                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31590                     break;
31591                 default :
31592                     break;
31593             }
31594             
31595             Roo.each(box, function(b,kk){
31596                 
31597                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31598                 
31599                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31600                 
31601             }, this);
31602             
31603         }, this);
31604         
31605     },
31606     
31607     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31608     {
31609         Roo.each(eItems, function(b,k){
31610             
31611             b.size = (k == 0) ? 'sm' : 'xs';
31612             b.x = (k == 0) ? 2 : 1;
31613             b.y = (k == 0) ? 2 : 1;
31614             
31615             b.el.position('absolute');
31616             
31617             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31618                 
31619             b.el.setWidth(width);
31620             
31621             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31622             
31623             b.el.setHeight(height);
31624             
31625         }, this);
31626
31627         var positions = [];
31628         
31629         positions.push({
31630             x : maxX - this.unitWidth * 2 - this.gutter,
31631             y : minY
31632         });
31633         
31634         positions.push({
31635             x : maxX - this.unitWidth,
31636             y : minY + (this.unitWidth + this.gutter) * 2
31637         });
31638         
31639         positions.push({
31640             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31641             y : minY
31642         });
31643         
31644         Roo.each(eItems, function(b,k){
31645             
31646             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31647
31648         }, this);
31649         
31650     },
31651     
31652     getVerticalOneBoxColPositions : function(x, y, box)
31653     {
31654         var pos = [];
31655         
31656         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31657         
31658         if(box[0].size == 'md-left'){
31659             rand = 0;
31660         }
31661         
31662         if(box[0].size == 'md-right'){
31663             rand = 1;
31664         }
31665         
31666         pos.push({
31667             x : x + (this.unitWidth + this.gutter) * rand,
31668             y : y
31669         });
31670         
31671         return pos;
31672     },
31673     
31674     getVerticalTwoBoxColPositions : function(x, y, box)
31675     {
31676         var pos = [];
31677         
31678         if(box[0].size == 'xs'){
31679             
31680             pos.push({
31681                 x : x,
31682                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31683             });
31684
31685             pos.push({
31686                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31687                 y : y
31688             });
31689             
31690             return pos;
31691             
31692         }
31693         
31694         pos.push({
31695             x : x,
31696             y : y
31697         });
31698
31699         pos.push({
31700             x : x + (this.unitWidth + this.gutter) * 2,
31701             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31702         });
31703         
31704         return pos;
31705         
31706     },
31707     
31708     getVerticalThreeBoxColPositions : function(x, y, box)
31709     {
31710         var pos = [];
31711         
31712         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31713             
31714             pos.push({
31715                 x : x,
31716                 y : y
31717             });
31718
31719             pos.push({
31720                 x : x + (this.unitWidth + this.gutter) * 1,
31721                 y : y
31722             });
31723             
31724             pos.push({
31725                 x : x + (this.unitWidth + this.gutter) * 2,
31726                 y : y
31727             });
31728             
31729             return pos;
31730             
31731         }
31732         
31733         if(box[0].size == 'xs' && box[1].size == 'xs'){
31734             
31735             pos.push({
31736                 x : x,
31737                 y : y
31738             });
31739
31740             pos.push({
31741                 x : x,
31742                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31743             });
31744             
31745             pos.push({
31746                 x : x + (this.unitWidth + this.gutter) * 1,
31747                 y : y
31748             });
31749             
31750             return pos;
31751             
31752         }
31753         
31754         pos.push({
31755             x : x,
31756             y : y
31757         });
31758
31759         pos.push({
31760             x : x + (this.unitWidth + this.gutter) * 2,
31761             y : y
31762         });
31763
31764         pos.push({
31765             x : x + (this.unitWidth + this.gutter) * 2,
31766             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31767         });
31768             
31769         return pos;
31770         
31771     },
31772     
31773     getVerticalFourBoxColPositions : function(x, y, box)
31774     {
31775         var pos = [];
31776         
31777         if(box[0].size == 'xs'){
31778             
31779             pos.push({
31780                 x : x,
31781                 y : y
31782             });
31783
31784             pos.push({
31785                 x : x,
31786                 y : y + (this.unitHeight + this.gutter) * 1
31787             });
31788             
31789             pos.push({
31790                 x : x,
31791                 y : y + (this.unitHeight + this.gutter) * 2
31792             });
31793             
31794             pos.push({
31795                 x : x + (this.unitWidth + this.gutter) * 1,
31796                 y : y
31797             });
31798             
31799             return pos;
31800             
31801         }
31802         
31803         pos.push({
31804             x : x,
31805             y : y
31806         });
31807
31808         pos.push({
31809             x : x + (this.unitWidth + this.gutter) * 2,
31810             y : y
31811         });
31812
31813         pos.push({
31814             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31815             y : y + (this.unitHeight + this.gutter) * 1
31816         });
31817
31818         pos.push({
31819             x : x + (this.unitWidth + this.gutter) * 2,
31820             y : y + (this.unitWidth + this.gutter) * 2
31821         });
31822
31823         return pos;
31824         
31825     },
31826     
31827     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31828     {
31829         var pos = [];
31830         
31831         if(box[0].size == 'md-left'){
31832             pos.push({
31833                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31834                 y : minY
31835             });
31836             
31837             return pos;
31838         }
31839         
31840         if(box[0].size == 'md-right'){
31841             pos.push({
31842                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31843                 y : minY + (this.unitWidth + this.gutter) * 1
31844             });
31845             
31846             return pos;
31847         }
31848         
31849         var rand = Math.floor(Math.random() * (4 - box[0].y));
31850         
31851         pos.push({
31852             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31853             y : minY + (this.unitWidth + this.gutter) * rand
31854         });
31855         
31856         return pos;
31857         
31858     },
31859     
31860     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31861     {
31862         var pos = [];
31863         
31864         if(box[0].size == 'xs'){
31865             
31866             pos.push({
31867                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31868                 y : minY
31869             });
31870
31871             pos.push({
31872                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31873                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31874             });
31875             
31876             return pos;
31877             
31878         }
31879         
31880         pos.push({
31881             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31882             y : minY
31883         });
31884
31885         pos.push({
31886             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31887             y : minY + (this.unitWidth + this.gutter) * 2
31888         });
31889         
31890         return pos;
31891         
31892     },
31893     
31894     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31895     {
31896         var pos = [];
31897         
31898         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31899             
31900             pos.push({
31901                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31902                 y : minY
31903             });
31904
31905             pos.push({
31906                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31907                 y : minY + (this.unitWidth + this.gutter) * 1
31908             });
31909             
31910             pos.push({
31911                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31912                 y : minY + (this.unitWidth + this.gutter) * 2
31913             });
31914             
31915             return pos;
31916             
31917         }
31918         
31919         if(box[0].size == 'xs' && box[1].size == 'xs'){
31920             
31921             pos.push({
31922                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31923                 y : minY
31924             });
31925
31926             pos.push({
31927                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31928                 y : minY
31929             });
31930             
31931             pos.push({
31932                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31933                 y : minY + (this.unitWidth + this.gutter) * 1
31934             });
31935             
31936             return pos;
31937             
31938         }
31939         
31940         pos.push({
31941             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31942             y : minY
31943         });
31944
31945         pos.push({
31946             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31947             y : minY + (this.unitWidth + this.gutter) * 2
31948         });
31949
31950         pos.push({
31951             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31952             y : minY + (this.unitWidth + this.gutter) * 2
31953         });
31954             
31955         return pos;
31956         
31957     },
31958     
31959     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31960     {
31961         var pos = [];
31962         
31963         if(box[0].size == 'xs'){
31964             
31965             pos.push({
31966                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31967                 y : minY
31968             });
31969
31970             pos.push({
31971                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31977                 y : minY
31978             });
31979             
31980             pos.push({
31981                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31982                 y : minY + (this.unitWidth + this.gutter) * 1
31983             });
31984             
31985             return pos;
31986             
31987         }
31988         
31989         pos.push({
31990             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31991             y : minY
31992         });
31993         
31994         pos.push({
31995             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31996             y : minY + (this.unitWidth + this.gutter) * 2
31997         });
31998         
31999         pos.push({
32000             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32006             y : minY + (this.unitWidth + this.gutter) * 2
32007         });
32008
32009         return pos;
32010         
32011     },
32012     
32013     /**
32014     * remove a Masonry Brick
32015     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32016     */
32017     removeBrick : function(brick_id)
32018     {
32019         if (!brick_id) {
32020             return;
32021         }
32022         
32023         for (var i = 0; i<this.bricks.length; i++) {
32024             if (this.bricks[i].id == brick_id) {
32025                 this.bricks.splice(i,1);
32026                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32027                 this.initial();
32028             }
32029         }
32030     },
32031     
32032     /**
32033     * adds a Masonry Brick
32034     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32035     */
32036     addBrick : function(cfg)
32037     {
32038         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32039         //this.register(cn);
32040         cn.parentId = this.id;
32041         cn.render(this.el);
32042         return cn;
32043     },
32044     
32045     /**
32046     * register a Masonry Brick
32047     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32048     */
32049     
32050     register : function(brick)
32051     {
32052         this.bricks.push(brick);
32053         brick.masonryId = this.id;
32054     },
32055     
32056     /**
32057     * clear all the Masonry Brick
32058     */
32059     clearAll : function()
32060     {
32061         this.bricks = [];
32062         //this.getChildContainer().dom.innerHTML = "";
32063         this.el.dom.innerHTML = '';
32064     },
32065     
32066     getSelected : function()
32067     {
32068         if (!this.selectedBrick) {
32069             return false;
32070         }
32071         
32072         return this.selectedBrick;
32073     }
32074 });
32075
32076 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32077     
32078     groups: {},
32079      /**
32080     * register a Masonry Layout
32081     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32082     */
32083     
32084     register : function(layout)
32085     {
32086         this.groups[layout.id] = layout;
32087     },
32088     /**
32089     * fetch a  Masonry Layout based on the masonry layout ID
32090     * @param {string} the masonry layout to add
32091     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32092     */
32093     
32094     get: function(layout_id) {
32095         if (typeof(this.groups[layout_id]) == 'undefined') {
32096             return false;
32097         }
32098         return this.groups[layout_id] ;
32099     }
32100     
32101     
32102     
32103 });
32104
32105  
32106
32107  /**
32108  *
32109  * This is based on 
32110  * http://masonry.desandro.com
32111  *
32112  * The idea is to render all the bricks based on vertical width...
32113  *
32114  * The original code extends 'outlayer' - we might need to use that....
32115  * 
32116  */
32117
32118
32119 /**
32120  * @class Roo.bootstrap.LayoutMasonryAuto
32121  * @extends Roo.bootstrap.Component
32122  * Bootstrap Layout Masonry class
32123  * 
32124  * @constructor
32125  * Create a new Element
32126  * @param {Object} config The config object
32127  */
32128
32129 Roo.bootstrap.LayoutMasonryAuto = function(config){
32130     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32131 };
32132
32133 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32134     
32135       /**
32136      * @cfg {Boolean} isFitWidth  - resize the width..
32137      */   
32138     isFitWidth : false,  // options..
32139     /**
32140      * @cfg {Boolean} isOriginLeft = left align?
32141      */   
32142     isOriginLeft : true,
32143     /**
32144      * @cfg {Boolean} isOriginTop = top align?
32145      */   
32146     isOriginTop : false,
32147     /**
32148      * @cfg {Boolean} isLayoutInstant = no animation?
32149      */   
32150     isLayoutInstant : false, // needed?
32151     /**
32152      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32153      */   
32154     isResizingContainer : true,
32155     /**
32156      * @cfg {Number} columnWidth  width of the columns 
32157      */   
32158     
32159     columnWidth : 0,
32160     
32161     /**
32162      * @cfg {Number} maxCols maximum number of columns
32163      */   
32164     
32165     maxCols: 0,
32166     /**
32167      * @cfg {Number} padHeight padding below box..
32168      */   
32169     
32170     padHeight : 10, 
32171     
32172     /**
32173      * @cfg {Boolean} isAutoInitial defalut true
32174      */   
32175     
32176     isAutoInitial : true, 
32177     
32178     // private?
32179     gutter : 0,
32180     
32181     containerWidth: 0,
32182     initialColumnWidth : 0,
32183     currentSize : null,
32184     
32185     colYs : null, // array.
32186     maxY : 0,
32187     padWidth: 10,
32188     
32189     
32190     tag: 'div',
32191     cls: '',
32192     bricks: null, //CompositeElement
32193     cols : 0, // array?
32194     // element : null, // wrapped now this.el
32195     _isLayoutInited : null, 
32196     
32197     
32198     getAutoCreate : function(){
32199         
32200         var cfg = {
32201             tag: this.tag,
32202             cls: 'blog-masonary-wrapper ' + this.cls,
32203             cn : {
32204                 cls : 'mas-boxes masonary'
32205             }
32206         };
32207         
32208         return cfg;
32209     },
32210     
32211     getChildContainer: function( )
32212     {
32213         if (this.boxesEl) {
32214             return this.boxesEl;
32215         }
32216         
32217         this.boxesEl = this.el.select('.mas-boxes').first();
32218         
32219         return this.boxesEl;
32220     },
32221     
32222     
32223     initEvents : function()
32224     {
32225         var _this = this;
32226         
32227         if(this.isAutoInitial){
32228             Roo.log('hook children rendered');
32229             this.on('childrenrendered', function() {
32230                 Roo.log('children rendered');
32231                 _this.initial();
32232             } ,this);
32233         }
32234         
32235     },
32236     
32237     initial : function()
32238     {
32239         this.reloadItems();
32240
32241         this.currentSize = this.el.getBox(true);
32242
32243         /// was window resize... - let's see if this works..
32244         Roo.EventManager.onWindowResize(this.resize, this); 
32245
32246         if(!this.isAutoInitial){
32247             this.layout();
32248             return;
32249         }
32250         
32251         this.layout.defer(500,this);
32252     },
32253     
32254     reloadItems: function()
32255     {
32256         this.bricks = this.el.select('.masonry-brick', true);
32257         
32258         this.bricks.each(function(b) {
32259             //Roo.log(b.getSize());
32260             if (!b.attr('originalwidth')) {
32261                 b.attr('originalwidth',  b.getSize().width);
32262             }
32263             
32264         });
32265         
32266         Roo.log(this.bricks.elements.length);
32267     },
32268     
32269     resize : function()
32270     {
32271         Roo.log('resize');
32272         var cs = this.el.getBox(true);
32273         
32274         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32275             Roo.log("no change in with or X");
32276             return;
32277         }
32278         this.currentSize = cs;
32279         this.layout();
32280     },
32281     
32282     layout : function()
32283     {
32284          Roo.log('layout');
32285         this._resetLayout();
32286         //this._manageStamps();
32287       
32288         // don't animate first layout
32289         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32290         this.layoutItems( isInstant );
32291       
32292         // flag for initalized
32293         this._isLayoutInited = true;
32294     },
32295     
32296     layoutItems : function( isInstant )
32297     {
32298         //var items = this._getItemsForLayout( this.items );
32299         // original code supports filtering layout items.. we just ignore it..
32300         
32301         this._layoutItems( this.bricks , isInstant );
32302       
32303         this._postLayout();
32304     },
32305     _layoutItems : function ( items , isInstant)
32306     {
32307        //this.fireEvent( 'layout', this, items );
32308     
32309
32310         if ( !items || !items.elements.length ) {
32311           // no items, emit event with empty array
32312             return;
32313         }
32314
32315         var queue = [];
32316         items.each(function(item) {
32317             Roo.log("layout item");
32318             Roo.log(item);
32319             // get x/y object from method
32320             var position = this._getItemLayoutPosition( item );
32321             // enqueue
32322             position.item = item;
32323             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32324             queue.push( position );
32325         }, this);
32326       
32327         this._processLayoutQueue( queue );
32328     },
32329     /** Sets position of item in DOM
32330     * @param {Element} item
32331     * @param {Number} x - horizontal position
32332     * @param {Number} y - vertical position
32333     * @param {Boolean} isInstant - disables transitions
32334     */
32335     _processLayoutQueue : function( queue )
32336     {
32337         for ( var i=0, len = queue.length; i < len; i++ ) {
32338             var obj = queue[i];
32339             obj.item.position('absolute');
32340             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32341         }
32342     },
32343       
32344     
32345     /**
32346     * Any logic you want to do after each layout,
32347     * i.e. size the container
32348     */
32349     _postLayout : function()
32350     {
32351         this.resizeContainer();
32352     },
32353     
32354     resizeContainer : function()
32355     {
32356         if ( !this.isResizingContainer ) {
32357             return;
32358         }
32359         var size = this._getContainerSize();
32360         if ( size ) {
32361             this.el.setSize(size.width,size.height);
32362             this.boxesEl.setSize(size.width,size.height);
32363         }
32364     },
32365     
32366     
32367     
32368     _resetLayout : function()
32369     {
32370         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32371         this.colWidth = this.el.getWidth();
32372         //this.gutter = this.el.getWidth(); 
32373         
32374         this.measureColumns();
32375
32376         // reset column Y
32377         var i = this.cols;
32378         this.colYs = [];
32379         while (i--) {
32380             this.colYs.push( 0 );
32381         }
32382     
32383         this.maxY = 0;
32384     },
32385
32386     measureColumns : function()
32387     {
32388         this.getContainerWidth();
32389       // if columnWidth is 0, default to outerWidth of first item
32390         if ( !this.columnWidth ) {
32391             var firstItem = this.bricks.first();
32392             Roo.log(firstItem);
32393             this.columnWidth  = this.containerWidth;
32394             if (firstItem && firstItem.attr('originalwidth') ) {
32395                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32396             }
32397             // columnWidth fall back to item of first element
32398             Roo.log("set column width?");
32399                         this.initialColumnWidth = this.columnWidth  ;
32400
32401             // if first elem has no width, default to size of container
32402             
32403         }
32404         
32405         
32406         if (this.initialColumnWidth) {
32407             this.columnWidth = this.initialColumnWidth;
32408         }
32409         
32410         
32411             
32412         // column width is fixed at the top - however if container width get's smaller we should
32413         // reduce it...
32414         
32415         // this bit calcs how man columns..
32416             
32417         var columnWidth = this.columnWidth += this.gutter;
32418       
32419         // calculate columns
32420         var containerWidth = this.containerWidth + this.gutter;
32421         
32422         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32423         // fix rounding errors, typically with gutters
32424         var excess = columnWidth - containerWidth % columnWidth;
32425         
32426         
32427         // if overshoot is less than a pixel, round up, otherwise floor it
32428         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32429         cols = Math[ mathMethod ]( cols );
32430         this.cols = Math.max( cols, 1 );
32431         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32432         
32433          // padding positioning..
32434         var totalColWidth = this.cols * this.columnWidth;
32435         var padavail = this.containerWidth - totalColWidth;
32436         // so for 2 columns - we need 3 'pads'
32437         
32438         var padNeeded = (1+this.cols) * this.padWidth;
32439         
32440         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32441         
32442         this.columnWidth += padExtra
32443         //this.padWidth = Math.floor(padavail /  ( this.cols));
32444         
32445         // adjust colum width so that padding is fixed??
32446         
32447         // we have 3 columns ... total = width * 3
32448         // we have X left over... that should be used by 
32449         
32450         //if (this.expandC) {
32451             
32452         //}
32453         
32454         
32455         
32456     },
32457     
32458     getContainerWidth : function()
32459     {
32460        /* // container is parent if fit width
32461         var container = this.isFitWidth ? this.element.parentNode : this.element;
32462         // check that this.size and size are there
32463         // IE8 triggers resize on body size change, so they might not be
32464         
32465         var size = getSize( container );  //FIXME
32466         this.containerWidth = size && size.innerWidth; //FIXME
32467         */
32468          
32469         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32470         
32471     },
32472     
32473     _getItemLayoutPosition : function( item )  // what is item?
32474     {
32475         // we resize the item to our columnWidth..
32476       
32477         item.setWidth(this.columnWidth);
32478         item.autoBoxAdjust  = false;
32479         
32480         var sz = item.getSize();
32481  
32482         // how many columns does this brick span
32483         var remainder = this.containerWidth % this.columnWidth;
32484         
32485         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32486         // round if off by 1 pixel, otherwise use ceil
32487         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32488         colSpan = Math.min( colSpan, this.cols );
32489         
32490         // normally this should be '1' as we dont' currently allow multi width columns..
32491         
32492         var colGroup = this._getColGroup( colSpan );
32493         // get the minimum Y value from the columns
32494         var minimumY = Math.min.apply( Math, colGroup );
32495         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32496         
32497         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32498          
32499         // position the brick
32500         var position = {
32501             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32502             y: this.currentSize.y + minimumY + this.padHeight
32503         };
32504         
32505         Roo.log(position);
32506         // apply setHeight to necessary columns
32507         var setHeight = minimumY + sz.height + this.padHeight;
32508         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32509         
32510         var setSpan = this.cols + 1 - colGroup.length;
32511         for ( var i = 0; i < setSpan; i++ ) {
32512           this.colYs[ shortColIndex + i ] = setHeight ;
32513         }
32514       
32515         return position;
32516     },
32517     
32518     /**
32519      * @param {Number} colSpan - number of columns the element spans
32520      * @returns {Array} colGroup
32521      */
32522     _getColGroup : function( colSpan )
32523     {
32524         if ( colSpan < 2 ) {
32525           // if brick spans only one column, use all the column Ys
32526           return this.colYs;
32527         }
32528       
32529         var colGroup = [];
32530         // how many different places could this brick fit horizontally
32531         var groupCount = this.cols + 1 - colSpan;
32532         // for each group potential horizontal position
32533         for ( var i = 0; i < groupCount; i++ ) {
32534           // make an array of colY values for that one group
32535           var groupColYs = this.colYs.slice( i, i + colSpan );
32536           // and get the max value of the array
32537           colGroup[i] = Math.max.apply( Math, groupColYs );
32538         }
32539         return colGroup;
32540     },
32541     /*
32542     _manageStamp : function( stamp )
32543     {
32544         var stampSize =  stamp.getSize();
32545         var offset = stamp.getBox();
32546         // get the columns that this stamp affects
32547         var firstX = this.isOriginLeft ? offset.x : offset.right;
32548         var lastX = firstX + stampSize.width;
32549         var firstCol = Math.floor( firstX / this.columnWidth );
32550         firstCol = Math.max( 0, firstCol );
32551         
32552         var lastCol = Math.floor( lastX / this.columnWidth );
32553         // lastCol should not go over if multiple of columnWidth #425
32554         lastCol -= lastX % this.columnWidth ? 0 : 1;
32555         lastCol = Math.min( this.cols - 1, lastCol );
32556         
32557         // set colYs to bottom of the stamp
32558         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32559             stampSize.height;
32560             
32561         for ( var i = firstCol; i <= lastCol; i++ ) {
32562           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32563         }
32564     },
32565     */
32566     
32567     _getContainerSize : function()
32568     {
32569         this.maxY = Math.max.apply( Math, this.colYs );
32570         var size = {
32571             height: this.maxY
32572         };
32573       
32574         if ( this.isFitWidth ) {
32575             size.width = this._getContainerFitWidth();
32576         }
32577       
32578         return size;
32579     },
32580     
32581     _getContainerFitWidth : function()
32582     {
32583         var unusedCols = 0;
32584         // count unused columns
32585         var i = this.cols;
32586         while ( --i ) {
32587           if ( this.colYs[i] !== 0 ) {
32588             break;
32589           }
32590           unusedCols++;
32591         }
32592         // fit container to columns that have been used
32593         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32594     },
32595     
32596     needsResizeLayout : function()
32597     {
32598         var previousWidth = this.containerWidth;
32599         this.getContainerWidth();
32600         return previousWidth !== this.containerWidth;
32601     }
32602  
32603 });
32604
32605  
32606
32607  /*
32608  * - LGPL
32609  *
32610  * element
32611  * 
32612  */
32613
32614 /**
32615  * @class Roo.bootstrap.MasonryBrick
32616  * @extends Roo.bootstrap.Component
32617  * Bootstrap MasonryBrick class
32618  * 
32619  * @constructor
32620  * Create a new MasonryBrick
32621  * @param {Object} config The config object
32622  */
32623
32624 Roo.bootstrap.MasonryBrick = function(config){
32625     
32626     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32627     
32628     Roo.bootstrap.MasonryBrick.register(this);
32629     
32630     this.addEvents({
32631         // raw events
32632         /**
32633          * @event click
32634          * When a MasonryBrick is clcik
32635          * @param {Roo.bootstrap.MasonryBrick} this
32636          * @param {Roo.EventObject} e
32637          */
32638         "click" : true
32639     });
32640 };
32641
32642 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32643     
32644     /**
32645      * @cfg {String} title
32646      */   
32647     title : '',
32648     /**
32649      * @cfg {String} html
32650      */   
32651     html : '',
32652     /**
32653      * @cfg {String} bgimage
32654      */   
32655     bgimage : '',
32656     /**
32657      * @cfg {String} videourl
32658      */   
32659     videourl : '',
32660     /**
32661      * @cfg {String} cls
32662      */   
32663     cls : '',
32664     /**
32665      * @cfg {String} href
32666      */   
32667     href : '',
32668     /**
32669      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32670      */   
32671     size : 'xs',
32672     
32673     /**
32674      * @cfg {String} placetitle (center|bottom)
32675      */   
32676     placetitle : '',
32677     
32678     /**
32679      * @cfg {Boolean} isFitContainer defalut true
32680      */   
32681     isFitContainer : true, 
32682     
32683     /**
32684      * @cfg {Boolean} preventDefault defalut false
32685      */   
32686     preventDefault : false, 
32687     
32688     /**
32689      * @cfg {Boolean} inverse defalut false
32690      */   
32691     maskInverse : false, 
32692     
32693     getAutoCreate : function()
32694     {
32695         if(!this.isFitContainer){
32696             return this.getSplitAutoCreate();
32697         }
32698         
32699         var cls = 'masonry-brick masonry-brick-full';
32700         
32701         if(this.href.length){
32702             cls += ' masonry-brick-link';
32703         }
32704         
32705         if(this.bgimage.length){
32706             cls += ' masonry-brick-image';
32707         }
32708         
32709         if(this.maskInverse){
32710             cls += ' mask-inverse';
32711         }
32712         
32713         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32714             cls += ' enable-mask';
32715         }
32716         
32717         if(this.size){
32718             cls += ' masonry-' + this.size + '-brick';
32719         }
32720         
32721         if(this.placetitle.length){
32722             
32723             switch (this.placetitle) {
32724                 case 'center' :
32725                     cls += ' masonry-center-title';
32726                     break;
32727                 case 'bottom' :
32728                     cls += ' masonry-bottom-title';
32729                     break;
32730                 default:
32731                     break;
32732             }
32733             
32734         } else {
32735             if(!this.html.length && !this.bgimage.length){
32736                 cls += ' masonry-center-title';
32737             }
32738
32739             if(!this.html.length && this.bgimage.length){
32740                 cls += ' masonry-bottom-title';
32741             }
32742         }
32743         
32744         if(this.cls){
32745             cls += ' ' + this.cls;
32746         }
32747         
32748         var cfg = {
32749             tag: (this.href.length) ? 'a' : 'div',
32750             cls: cls,
32751             cn: [
32752                 {
32753                     tag: 'div',
32754                     cls: 'masonry-brick-mask'
32755                 },
32756                 {
32757                     tag: 'div',
32758                     cls: 'masonry-brick-paragraph',
32759                     cn: []
32760                 }
32761             ]
32762         };
32763         
32764         if(this.href.length){
32765             cfg.href = this.href;
32766         }
32767         
32768         var cn = cfg.cn[1].cn;
32769         
32770         if(this.title.length){
32771             cn.push({
32772                 tag: 'h4',
32773                 cls: 'masonry-brick-title',
32774                 html: this.title
32775             });
32776         }
32777         
32778         if(this.html.length){
32779             cn.push({
32780                 tag: 'p',
32781                 cls: 'masonry-brick-text',
32782                 html: this.html
32783             });
32784         }
32785         
32786         if (!this.title.length && !this.html.length) {
32787             cfg.cn[1].cls += ' hide';
32788         }
32789         
32790         if(this.bgimage.length){
32791             cfg.cn.push({
32792                 tag: 'img',
32793                 cls: 'masonry-brick-image-view',
32794                 src: this.bgimage
32795             });
32796         }
32797         
32798         if(this.videourl.length){
32799             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32800             // youtube support only?
32801             cfg.cn.push({
32802                 tag: 'iframe',
32803                 cls: 'masonry-brick-image-view',
32804                 src: vurl,
32805                 frameborder : 0,
32806                 allowfullscreen : true
32807             });
32808         }
32809         
32810         return cfg;
32811         
32812     },
32813     
32814     getSplitAutoCreate : function()
32815     {
32816         var cls = 'masonry-brick masonry-brick-split';
32817         
32818         if(this.href.length){
32819             cls += ' masonry-brick-link';
32820         }
32821         
32822         if(this.bgimage.length){
32823             cls += ' masonry-brick-image';
32824         }
32825         
32826         if(this.size){
32827             cls += ' masonry-' + this.size + '-brick';
32828         }
32829         
32830         switch (this.placetitle) {
32831             case 'center' :
32832                 cls += ' masonry-center-title';
32833                 break;
32834             case 'bottom' :
32835                 cls += ' masonry-bottom-title';
32836                 break;
32837             default:
32838                 if(!this.bgimage.length){
32839                     cls += ' masonry-center-title';
32840                 }
32841
32842                 if(this.bgimage.length){
32843                     cls += ' masonry-bottom-title';
32844                 }
32845                 break;
32846         }
32847         
32848         if(this.cls){
32849             cls += ' ' + this.cls;
32850         }
32851         
32852         var cfg = {
32853             tag: (this.href.length) ? 'a' : 'div',
32854             cls: cls,
32855             cn: [
32856                 {
32857                     tag: 'div',
32858                     cls: 'masonry-brick-split-head',
32859                     cn: [
32860                         {
32861                             tag: 'div',
32862                             cls: 'masonry-brick-paragraph',
32863                             cn: []
32864                         }
32865                     ]
32866                 },
32867                 {
32868                     tag: 'div',
32869                     cls: 'masonry-brick-split-body',
32870                     cn: []
32871                 }
32872             ]
32873         };
32874         
32875         if(this.href.length){
32876             cfg.href = this.href;
32877         }
32878         
32879         if(this.title.length){
32880             cfg.cn[0].cn[0].cn.push({
32881                 tag: 'h4',
32882                 cls: 'masonry-brick-title',
32883                 html: this.title
32884             });
32885         }
32886         
32887         if(this.html.length){
32888             cfg.cn[1].cn.push({
32889                 tag: 'p',
32890                 cls: 'masonry-brick-text',
32891                 html: this.html
32892             });
32893         }
32894
32895         if(this.bgimage.length){
32896             cfg.cn[0].cn.push({
32897                 tag: 'img',
32898                 cls: 'masonry-brick-image-view',
32899                 src: this.bgimage
32900             });
32901         }
32902         
32903         if(this.videourl.length){
32904             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32905             // youtube support only?
32906             cfg.cn[0].cn.cn.push({
32907                 tag: 'iframe',
32908                 cls: 'masonry-brick-image-view',
32909                 src: vurl,
32910                 frameborder : 0,
32911                 allowfullscreen : true
32912             });
32913         }
32914         
32915         return cfg;
32916     },
32917     
32918     initEvents: function() 
32919     {
32920         switch (this.size) {
32921             case 'xs' :
32922                 this.x = 1;
32923                 this.y = 1;
32924                 break;
32925             case 'sm' :
32926                 this.x = 2;
32927                 this.y = 2;
32928                 break;
32929             case 'md' :
32930             case 'md-left' :
32931             case 'md-right' :
32932                 this.x = 3;
32933                 this.y = 3;
32934                 break;
32935             case 'tall' :
32936                 this.x = 2;
32937                 this.y = 3;
32938                 break;
32939             case 'wide' :
32940                 this.x = 3;
32941                 this.y = 2;
32942                 break;
32943             case 'wide-thin' :
32944                 this.x = 3;
32945                 this.y = 1;
32946                 break;
32947                         
32948             default :
32949                 break;
32950         }
32951         
32952         if(Roo.isTouch){
32953             this.el.on('touchstart', this.onTouchStart, this);
32954             this.el.on('touchmove', this.onTouchMove, this);
32955             this.el.on('touchend', this.onTouchEnd, this);
32956             this.el.on('contextmenu', this.onContextMenu, this);
32957         } else {
32958             this.el.on('mouseenter'  ,this.enter, this);
32959             this.el.on('mouseleave', this.leave, this);
32960             this.el.on('click', this.onClick, this);
32961         }
32962         
32963         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32964             this.parent().bricks.push(this);   
32965         }
32966         
32967     },
32968     
32969     onClick: function(e, el)
32970     {
32971         var time = this.endTimer - this.startTimer;
32972         // Roo.log(e.preventDefault());
32973         if(Roo.isTouch){
32974             if(time > 1000){
32975                 e.preventDefault();
32976                 return;
32977             }
32978         }
32979         
32980         if(!this.preventDefault){
32981             return;
32982         }
32983         
32984         e.preventDefault();
32985         
32986         if (this.activeClass != '') {
32987             this.selectBrick();
32988         }
32989         
32990         this.fireEvent('click', this, e);
32991     },
32992     
32993     enter: function(e, el)
32994     {
32995         e.preventDefault();
32996         
32997         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32998             return;
32999         }
33000         
33001         if(this.bgimage.length && this.html.length){
33002             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33003         }
33004     },
33005     
33006     leave: function(e, el)
33007     {
33008         e.preventDefault();
33009         
33010         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33011             return;
33012         }
33013         
33014         if(this.bgimage.length && this.html.length){
33015             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33016         }
33017     },
33018     
33019     onTouchStart: function(e, el)
33020     {
33021 //        e.preventDefault();
33022         
33023         this.touchmoved = false;
33024         
33025         if(!this.isFitContainer){
33026             return;
33027         }
33028         
33029         if(!this.bgimage.length || !this.html.length){
33030             return;
33031         }
33032         
33033         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33034         
33035         this.timer = new Date().getTime();
33036         
33037     },
33038     
33039     onTouchMove: function(e, el)
33040     {
33041         this.touchmoved = true;
33042     },
33043     
33044     onContextMenu : function(e,el)
33045     {
33046         e.preventDefault();
33047         e.stopPropagation();
33048         return false;
33049     },
33050     
33051     onTouchEnd: function(e, el)
33052     {
33053 //        e.preventDefault();
33054         
33055         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33056         
33057             this.leave(e,el);
33058             
33059             return;
33060         }
33061         
33062         if(!this.bgimage.length || !this.html.length){
33063             
33064             if(this.href.length){
33065                 window.location.href = this.href;
33066             }
33067             
33068             return;
33069         }
33070         
33071         if(!this.isFitContainer){
33072             return;
33073         }
33074         
33075         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33076         
33077         window.location.href = this.href;
33078     },
33079     
33080     //selection on single brick only
33081     selectBrick : function() {
33082         
33083         if (!this.parentId) {
33084             return;
33085         }
33086         
33087         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33088         var index = m.selectedBrick.indexOf(this.id);
33089         
33090         if ( index > -1) {
33091             m.selectedBrick.splice(index,1);
33092             this.el.removeClass(this.activeClass);
33093             return;
33094         }
33095         
33096         for(var i = 0; i < m.selectedBrick.length; i++) {
33097             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33098             b.el.removeClass(b.activeClass);
33099         }
33100         
33101         m.selectedBrick = [];
33102         
33103         m.selectedBrick.push(this.id);
33104         this.el.addClass(this.activeClass);
33105         return;
33106     },
33107     
33108     isSelected : function(){
33109         return this.el.hasClass(this.activeClass);
33110         
33111     }
33112 });
33113
33114 Roo.apply(Roo.bootstrap.MasonryBrick, {
33115     
33116     //groups: {},
33117     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33118      /**
33119     * register a Masonry Brick
33120     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33121     */
33122     
33123     register : function(brick)
33124     {
33125         //this.groups[brick.id] = brick;
33126         this.groups.add(brick.id, brick);
33127     },
33128     /**
33129     * fetch a  masonry brick based on the masonry brick ID
33130     * @param {string} the masonry brick to add
33131     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33132     */
33133     
33134     get: function(brick_id) 
33135     {
33136         // if (typeof(this.groups[brick_id]) == 'undefined') {
33137         //     return false;
33138         // }
33139         // return this.groups[brick_id] ;
33140         
33141         if(this.groups.key(brick_id)) {
33142             return this.groups.key(brick_id);
33143         }
33144         
33145         return false;
33146     }
33147     
33148     
33149     
33150 });
33151
33152  /*
33153  * - LGPL
33154  *
33155  * element
33156  * 
33157  */
33158
33159 /**
33160  * @class Roo.bootstrap.Brick
33161  * @extends Roo.bootstrap.Component
33162  * Bootstrap Brick class
33163  * 
33164  * @constructor
33165  * Create a new Brick
33166  * @param {Object} config The config object
33167  */
33168
33169 Roo.bootstrap.Brick = function(config){
33170     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33171     
33172     this.addEvents({
33173         // raw events
33174         /**
33175          * @event click
33176          * When a Brick is click
33177          * @param {Roo.bootstrap.Brick} this
33178          * @param {Roo.EventObject} e
33179          */
33180         "click" : true
33181     });
33182 };
33183
33184 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33185     
33186     /**
33187      * @cfg {String} title
33188      */   
33189     title : '',
33190     /**
33191      * @cfg {String} html
33192      */   
33193     html : '',
33194     /**
33195      * @cfg {String} bgimage
33196      */   
33197     bgimage : '',
33198     /**
33199      * @cfg {String} cls
33200      */   
33201     cls : '',
33202     /**
33203      * @cfg {String} href
33204      */   
33205     href : '',
33206     /**
33207      * @cfg {String} video
33208      */   
33209     video : '',
33210     /**
33211      * @cfg {Boolean} square
33212      */   
33213     square : true,
33214     
33215     getAutoCreate : function()
33216     {
33217         var cls = 'roo-brick';
33218         
33219         if(this.href.length){
33220             cls += ' roo-brick-link';
33221         }
33222         
33223         if(this.bgimage.length){
33224             cls += ' roo-brick-image';
33225         }
33226         
33227         if(!this.html.length && !this.bgimage.length){
33228             cls += ' roo-brick-center-title';
33229         }
33230         
33231         if(!this.html.length && this.bgimage.length){
33232             cls += ' roo-brick-bottom-title';
33233         }
33234         
33235         if(this.cls){
33236             cls += ' ' + this.cls;
33237         }
33238         
33239         var cfg = {
33240             tag: (this.href.length) ? 'a' : 'div',
33241             cls: cls,
33242             cn: [
33243                 {
33244                     tag: 'div',
33245                     cls: 'roo-brick-paragraph',
33246                     cn: []
33247                 }
33248             ]
33249         };
33250         
33251         if(this.href.length){
33252             cfg.href = this.href;
33253         }
33254         
33255         var cn = cfg.cn[0].cn;
33256         
33257         if(this.title.length){
33258             cn.push({
33259                 tag: 'h4',
33260                 cls: 'roo-brick-title',
33261                 html: this.title
33262             });
33263         }
33264         
33265         if(this.html.length){
33266             cn.push({
33267                 tag: 'p',
33268                 cls: 'roo-brick-text',
33269                 html: this.html
33270             });
33271         } else {
33272             cn.cls += ' hide';
33273         }
33274         
33275         if(this.bgimage.length){
33276             cfg.cn.push({
33277                 tag: 'img',
33278                 cls: 'roo-brick-image-view',
33279                 src: this.bgimage
33280             });
33281         }
33282         
33283         return cfg;
33284     },
33285     
33286     initEvents: function() 
33287     {
33288         if(this.title.length || this.html.length){
33289             this.el.on('mouseenter'  ,this.enter, this);
33290             this.el.on('mouseleave', this.leave, this);
33291         }
33292         
33293         Roo.EventManager.onWindowResize(this.resize, this); 
33294         
33295         if(this.bgimage.length){
33296             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33297             this.imageEl.on('load', this.onImageLoad, this);
33298             return;
33299         }
33300         
33301         this.resize();
33302     },
33303     
33304     onImageLoad : function()
33305     {
33306         this.resize();
33307     },
33308     
33309     resize : function()
33310     {
33311         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33312         
33313         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33314         
33315         if(this.bgimage.length){
33316             var image = this.el.select('.roo-brick-image-view', true).first();
33317             
33318             image.setWidth(paragraph.getWidth());
33319             
33320             if(this.square){
33321                 image.setHeight(paragraph.getWidth());
33322             }
33323             
33324             this.el.setHeight(image.getHeight());
33325             paragraph.setHeight(image.getHeight());
33326             
33327         }
33328         
33329     },
33330     
33331     enter: function(e, el)
33332     {
33333         e.preventDefault();
33334         
33335         if(this.bgimage.length){
33336             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33337             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33338         }
33339     },
33340     
33341     leave: function(e, el)
33342     {
33343         e.preventDefault();
33344         
33345         if(this.bgimage.length){
33346             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33347             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33348         }
33349     }
33350     
33351 });
33352
33353  
33354
33355  /*
33356  * - LGPL
33357  *
33358  * Number field 
33359  */
33360
33361 /**
33362  * @class Roo.bootstrap.NumberField
33363  * @extends Roo.bootstrap.Input
33364  * Bootstrap NumberField class
33365  * 
33366  * 
33367  * 
33368  * 
33369  * @constructor
33370  * Create a new NumberField
33371  * @param {Object} config The config object
33372  */
33373
33374 Roo.bootstrap.NumberField = function(config){
33375     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33376 };
33377
33378 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33379     
33380     /**
33381      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33382      */
33383     allowDecimals : true,
33384     /**
33385      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33386      */
33387     decimalSeparator : ".",
33388     /**
33389      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33390      */
33391     decimalPrecision : 2,
33392     /**
33393      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33394      */
33395     allowNegative : true,
33396     
33397     /**
33398      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33399      */
33400     allowZero: true,
33401     /**
33402      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33403      */
33404     minValue : Number.NEGATIVE_INFINITY,
33405     /**
33406      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33407      */
33408     maxValue : Number.MAX_VALUE,
33409     /**
33410      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33411      */
33412     minText : "The minimum value for this field is {0}",
33413     /**
33414      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33415      */
33416     maxText : "The maximum value for this field is {0}",
33417     /**
33418      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33419      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33420      */
33421     nanText : "{0} is not a valid number",
33422     /**
33423      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33424      */
33425     thousandsDelimiter : false,
33426     /**
33427      * @cfg {String} valueAlign alignment of value
33428      */
33429     valueAlign : "left",
33430
33431     getAutoCreate : function()
33432     {
33433         var hiddenInput = {
33434             tag: 'input',
33435             type: 'hidden',
33436             id: Roo.id(),
33437             cls: 'hidden-number-input'
33438         };
33439         
33440         if (this.name) {
33441             hiddenInput.name = this.name;
33442         }
33443         
33444         this.name = '';
33445         
33446         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33447         
33448         this.name = hiddenInput.name;
33449         
33450         if(cfg.cn.length > 0) {
33451             cfg.cn.push(hiddenInput);
33452         }
33453         
33454         return cfg;
33455     },
33456
33457     // private
33458     initEvents : function()
33459     {   
33460         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33461         
33462         var allowed = "0123456789";
33463         
33464         if(this.allowDecimals){
33465             allowed += this.decimalSeparator;
33466         }
33467         
33468         if(this.allowNegative){
33469             allowed += "-";
33470         }
33471         
33472         if(this.thousandsDelimiter) {
33473             allowed += ",";
33474         }
33475         
33476         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33477         
33478         var keyPress = function(e){
33479             
33480             var k = e.getKey();
33481             
33482             var c = e.getCharCode();
33483             
33484             if(
33485                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33486                     allowed.indexOf(String.fromCharCode(c)) === -1
33487             ){
33488                 e.stopEvent();
33489                 return;
33490             }
33491             
33492             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33493                 return;
33494             }
33495             
33496             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33497                 e.stopEvent();
33498             }
33499         };
33500         
33501         this.el.on("keypress", keyPress, this);
33502     },
33503     
33504     validateValue : function(value)
33505     {
33506         
33507         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33508             return false;
33509         }
33510         
33511         var num = this.parseValue(value);
33512         
33513         if(isNaN(num)){
33514             this.markInvalid(String.format(this.nanText, value));
33515             return false;
33516         }
33517         
33518         if(num < this.minValue){
33519             this.markInvalid(String.format(this.minText, this.minValue));
33520             return false;
33521         }
33522         
33523         if(num > this.maxValue){
33524             this.markInvalid(String.format(this.maxText, this.maxValue));
33525             return false;
33526         }
33527         
33528         return true;
33529     },
33530
33531     getValue : function()
33532     {
33533         var v = this.hiddenEl().getValue();
33534         
33535         return this.fixPrecision(this.parseValue(v));
33536     },
33537
33538     parseValue : function(value)
33539     {
33540         if(this.thousandsDelimiter) {
33541             value += "";
33542             r = new RegExp(",", "g");
33543             value = value.replace(r, "");
33544         }
33545         
33546         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33547         return isNaN(value) ? '' : value;
33548     },
33549
33550     fixPrecision : function(value)
33551     {
33552         if(this.thousandsDelimiter) {
33553             value += "";
33554             r = new RegExp(",", "g");
33555             value = value.replace(r, "");
33556         }
33557         
33558         var nan = isNaN(value);
33559         
33560         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33561             return nan ? '' : value;
33562         }
33563         return parseFloat(value).toFixed(this.decimalPrecision);
33564     },
33565
33566     setValue : function(v)
33567     {
33568         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33569         
33570         this.value = v;
33571         
33572         if(this.rendered){
33573             
33574             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33575             
33576             this.inputEl().dom.value = (v == '') ? '' :
33577                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33578             
33579             if(!this.allowZero && v === '0') {
33580                 this.hiddenEl().dom.value = '';
33581                 this.inputEl().dom.value = '';
33582             }
33583             
33584             this.validate();
33585         }
33586     },
33587
33588     decimalPrecisionFcn : function(v)
33589     {
33590         return Math.floor(v);
33591     },
33592
33593     beforeBlur : function()
33594     {
33595         var v = this.parseValue(this.getRawValue());
33596         
33597         if(v || v === 0 || v === ''){
33598             this.setValue(v);
33599         }
33600     },
33601     
33602     hiddenEl : function()
33603     {
33604         return this.el.select('input.hidden-number-input',true).first();
33605     }
33606     
33607 });
33608
33609  
33610
33611 /*
33612 * Licence: LGPL
33613 */
33614
33615 /**
33616  * @class Roo.bootstrap.DocumentSlider
33617  * @extends Roo.bootstrap.Component
33618  * Bootstrap DocumentSlider class
33619  * 
33620  * @constructor
33621  * Create a new DocumentViewer
33622  * @param {Object} config The config object
33623  */
33624
33625 Roo.bootstrap.DocumentSlider = function(config){
33626     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33627     
33628     this.files = [];
33629     
33630     this.addEvents({
33631         /**
33632          * @event initial
33633          * Fire after initEvent
33634          * @param {Roo.bootstrap.DocumentSlider} this
33635          */
33636         "initial" : true,
33637         /**
33638          * @event update
33639          * Fire after update
33640          * @param {Roo.bootstrap.DocumentSlider} this
33641          */
33642         "update" : true,
33643         /**
33644          * @event click
33645          * Fire after click
33646          * @param {Roo.bootstrap.DocumentSlider} this
33647          */
33648         "click" : true
33649     });
33650 };
33651
33652 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33653     
33654     files : false,
33655     
33656     indicator : 0,
33657     
33658     getAutoCreate : function()
33659     {
33660         var cfg = {
33661             tag : 'div',
33662             cls : 'roo-document-slider',
33663             cn : [
33664                 {
33665                     tag : 'div',
33666                     cls : 'roo-document-slider-header',
33667                     cn : [
33668                         {
33669                             tag : 'div',
33670                             cls : 'roo-document-slider-header-title'
33671                         }
33672                     ]
33673                 },
33674                 {
33675                     tag : 'div',
33676                     cls : 'roo-document-slider-body',
33677                     cn : [
33678                         {
33679                             tag : 'div',
33680                             cls : 'roo-document-slider-prev',
33681                             cn : [
33682                                 {
33683                                     tag : 'i',
33684                                     cls : 'fa fa-chevron-left'
33685                                 }
33686                             ]
33687                         },
33688                         {
33689                             tag : 'div',
33690                             cls : 'roo-document-slider-thumb',
33691                             cn : [
33692                                 {
33693                                     tag : 'img',
33694                                     cls : 'roo-document-slider-image'
33695                                 }
33696                             ]
33697                         },
33698                         {
33699                             tag : 'div',
33700                             cls : 'roo-document-slider-next',
33701                             cn : [
33702                                 {
33703                                     tag : 'i',
33704                                     cls : 'fa fa-chevron-right'
33705                                 }
33706                             ]
33707                         }
33708                     ]
33709                 }
33710             ]
33711         };
33712         
33713         return cfg;
33714     },
33715     
33716     initEvents : function()
33717     {
33718         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33719         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33720         
33721         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33722         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33723         
33724         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33725         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33726         
33727         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33728         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33729         
33730         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33731         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33732         
33733         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33734         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33735         
33736         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33737         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33738         
33739         this.thumbEl.on('click', this.onClick, this);
33740         
33741         this.prevIndicator.on('click', this.prev, this);
33742         
33743         this.nextIndicator.on('click', this.next, this);
33744         
33745     },
33746     
33747     initial : function()
33748     {
33749         if(this.files.length){
33750             this.indicator = 1;
33751             this.update()
33752         }
33753         
33754         this.fireEvent('initial', this);
33755     },
33756     
33757     update : function()
33758     {
33759         this.imageEl.attr('src', this.files[this.indicator - 1]);
33760         
33761         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33762         
33763         this.prevIndicator.show();
33764         
33765         if(this.indicator == 1){
33766             this.prevIndicator.hide();
33767         }
33768         
33769         this.nextIndicator.show();
33770         
33771         if(this.indicator == this.files.length){
33772             this.nextIndicator.hide();
33773         }
33774         
33775         this.thumbEl.scrollTo('top');
33776         
33777         this.fireEvent('update', this);
33778     },
33779     
33780     onClick : function(e)
33781     {
33782         e.preventDefault();
33783         
33784         this.fireEvent('click', this);
33785     },
33786     
33787     prev : function(e)
33788     {
33789         e.preventDefault();
33790         
33791         this.indicator = Math.max(1, this.indicator - 1);
33792         
33793         this.update();
33794     },
33795     
33796     next : function(e)
33797     {
33798         e.preventDefault();
33799         
33800         this.indicator = Math.min(this.files.length, this.indicator + 1);
33801         
33802         this.update();
33803     }
33804 });
33805 /*
33806  * - LGPL
33807  *
33808  * RadioSet
33809  *
33810  *
33811  */
33812
33813 /**
33814  * @class Roo.bootstrap.RadioSet
33815  * @extends Roo.bootstrap.Input
33816  * Bootstrap RadioSet class
33817  * @cfg {String} indicatorpos (left|right) default left
33818  * @cfg {Boolean} inline (true|false) inline the element (default true)
33819  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33820  * @constructor
33821  * Create a new RadioSet
33822  * @param {Object} config The config object
33823  */
33824
33825 Roo.bootstrap.RadioSet = function(config){
33826     
33827     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33828     
33829     this.radioes = [];
33830     
33831     Roo.bootstrap.RadioSet.register(this);
33832     
33833     this.addEvents({
33834         /**
33835         * @event check
33836         * Fires when the element is checked or unchecked.
33837         * @param {Roo.bootstrap.RadioSet} this This radio
33838         * @param {Roo.bootstrap.Radio} item The checked item
33839         */
33840        check : true,
33841        /**
33842         * @event click
33843         * Fires when the element is click.
33844         * @param {Roo.bootstrap.RadioSet} this This radio set
33845         * @param {Roo.bootstrap.Radio} item The checked item
33846         * @param {Roo.EventObject} e The event object
33847         */
33848        click : true
33849     });
33850     
33851 };
33852
33853 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33854
33855     radioes : false,
33856     
33857     inline : true,
33858     
33859     weight : '',
33860     
33861     indicatorpos : 'left',
33862     
33863     getAutoCreate : function()
33864     {
33865         var label = {
33866             tag : 'label',
33867             cls : 'roo-radio-set-label',
33868             cn : [
33869                 {
33870                     tag : 'span',
33871                     html : this.fieldLabel
33872                 }
33873             ]
33874         };
33875         
33876         if(this.indicatorpos == 'left'){
33877             label.cn.unshift({
33878                 tag : 'i',
33879                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33880                 tooltip : 'This field is required'
33881             });
33882         } else {
33883             label.cn.push({
33884                 tag : 'i',
33885                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33886                 tooltip : 'This field is required'
33887             });
33888         }
33889         
33890         var items = {
33891             tag : 'div',
33892             cls : 'roo-radio-set-items'
33893         };
33894         
33895         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33896         
33897         if (align === 'left' && this.fieldLabel.length) {
33898             
33899             items = {
33900                 cls : "roo-radio-set-right", 
33901                 cn: [
33902                     items
33903                 ]
33904             };
33905             
33906             if(this.labelWidth > 12){
33907                 label.style = "width: " + this.labelWidth + 'px';
33908             }
33909             
33910             if(this.labelWidth < 13 && this.labelmd == 0){
33911                 this.labelmd = this.labelWidth;
33912             }
33913             
33914             if(this.labellg > 0){
33915                 label.cls += ' col-lg-' + this.labellg;
33916                 items.cls += ' col-lg-' + (12 - this.labellg);
33917             }
33918             
33919             if(this.labelmd > 0){
33920                 label.cls += ' col-md-' + this.labelmd;
33921                 items.cls += ' col-md-' + (12 - this.labelmd);
33922             }
33923             
33924             if(this.labelsm > 0){
33925                 label.cls += ' col-sm-' + this.labelsm;
33926                 items.cls += ' col-sm-' + (12 - this.labelsm);
33927             }
33928             
33929             if(this.labelxs > 0){
33930                 label.cls += ' col-xs-' + this.labelxs;
33931                 items.cls += ' col-xs-' + (12 - this.labelxs);
33932             }
33933         }
33934         
33935         var cfg = {
33936             tag : 'div',
33937             cls : 'roo-radio-set',
33938             cn : [
33939                 {
33940                     tag : 'input',
33941                     cls : 'roo-radio-set-input',
33942                     type : 'hidden',
33943                     name : this.name,
33944                     value : this.value ? this.value :  ''
33945                 },
33946                 label,
33947                 items
33948             ]
33949         };
33950         
33951         if(this.weight.length){
33952             cfg.cls += ' roo-radio-' + this.weight;
33953         }
33954         
33955         if(this.inline) {
33956             cfg.cls += ' roo-radio-set-inline';
33957         }
33958         
33959         var settings=this;
33960         ['xs','sm','md','lg'].map(function(size){
33961             if (settings[size]) {
33962                 cfg.cls += ' col-' + size + '-' + settings[size];
33963             }
33964         });
33965         
33966         return cfg;
33967         
33968     },
33969
33970     initEvents : function()
33971     {
33972         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33973         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33974         
33975         if(!this.fieldLabel.length){
33976             this.labelEl.hide();
33977         }
33978         
33979         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33980         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33981         
33982         this.indicator = this.indicatorEl();
33983         
33984         if(this.indicator){
33985             this.indicator.addClass('invisible');
33986         }
33987         
33988         this.originalValue = this.getValue();
33989         
33990     },
33991     
33992     inputEl: function ()
33993     {
33994         return this.el.select('.roo-radio-set-input', true).first();
33995     },
33996     
33997     getChildContainer : function()
33998     {
33999         return this.itemsEl;
34000     },
34001     
34002     register : function(item)
34003     {
34004         this.radioes.push(item);
34005         
34006     },
34007     
34008     validate : function()
34009     {   
34010         if(this.getVisibilityEl().hasClass('hidden')){
34011             return true;
34012         }
34013         
34014         var valid = false;
34015         
34016         Roo.each(this.radioes, function(i){
34017             if(!i.checked){
34018                 return;
34019             }
34020             
34021             valid = true;
34022             return false;
34023         });
34024         
34025         if(this.allowBlank) {
34026             return true;
34027         }
34028         
34029         if(this.disabled || valid){
34030             this.markValid();
34031             return true;
34032         }
34033         
34034         this.markInvalid();
34035         return false;
34036         
34037     },
34038     
34039     markValid : function()
34040     {
34041         if(this.labelEl.isVisible(true)){
34042             this.indicatorEl().removeClass('visible');
34043             this.indicatorEl().addClass('invisible');
34044         }
34045         
34046         this.el.removeClass([this.invalidClass, this.validClass]);
34047         this.el.addClass(this.validClass);
34048         
34049         this.fireEvent('valid', this);
34050     },
34051     
34052     markInvalid : function(msg)
34053     {
34054         if(this.allowBlank || this.disabled){
34055             return;
34056         }
34057         
34058         if(this.labelEl.isVisible(true)){
34059             this.indicatorEl().removeClass('invisible');
34060             this.indicatorEl().addClass('visible');
34061         }
34062         
34063         this.el.removeClass([this.invalidClass, this.validClass]);
34064         this.el.addClass(this.invalidClass);
34065         
34066         this.fireEvent('invalid', this, msg);
34067         
34068     },
34069     
34070     setValue : function(v, suppressEvent)
34071     {   
34072         if(this.value === v){
34073             return;
34074         }
34075         
34076         this.value = v;
34077         
34078         if(this.rendered){
34079             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34080         }
34081         
34082         Roo.each(this.radioes, function(i){
34083             i.checked = false;
34084             i.el.removeClass('checked');
34085         });
34086         
34087         Roo.each(this.radioes, function(i){
34088             
34089             if(i.value === v || i.value.toString() === v.toString()){
34090                 i.checked = true;
34091                 i.el.addClass('checked');
34092                 
34093                 if(suppressEvent !== true){
34094                     this.fireEvent('check', this, i);
34095                 }
34096                 
34097                 return false;
34098             }
34099             
34100         }, this);
34101         
34102         this.validate();
34103     },
34104     
34105     clearInvalid : function(){
34106         
34107         if(!this.el || this.preventMark){
34108             return;
34109         }
34110         
34111         this.el.removeClass([this.invalidClass]);
34112         
34113         this.fireEvent('valid', this);
34114     }
34115     
34116 });
34117
34118 Roo.apply(Roo.bootstrap.RadioSet, {
34119     
34120     groups: {},
34121     
34122     register : function(set)
34123     {
34124         this.groups[set.name] = set;
34125     },
34126     
34127     get: function(name) 
34128     {
34129         if (typeof(this.groups[name]) == 'undefined') {
34130             return false;
34131         }
34132         
34133         return this.groups[name] ;
34134     }
34135     
34136 });
34137 /*
34138  * Based on:
34139  * Ext JS Library 1.1.1
34140  * Copyright(c) 2006-2007, Ext JS, LLC.
34141  *
34142  * Originally Released Under LGPL - original licence link has changed is not relivant.
34143  *
34144  * Fork - LGPL
34145  * <script type="text/javascript">
34146  */
34147
34148
34149 /**
34150  * @class Roo.bootstrap.SplitBar
34151  * @extends Roo.util.Observable
34152  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34153  * <br><br>
34154  * Usage:
34155  * <pre><code>
34156 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34157                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34158 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34159 split.minSize = 100;
34160 split.maxSize = 600;
34161 split.animate = true;
34162 split.on('moved', splitterMoved);
34163 </code></pre>
34164  * @constructor
34165  * Create a new SplitBar
34166  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34167  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34168  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34169  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34170                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34171                         position of the SplitBar).
34172  */
34173 Roo.bootstrap.SplitBar = function(cfg){
34174     
34175     /** @private */
34176     
34177     //{
34178     //  dragElement : elm
34179     //  resizingElement: el,
34180         // optional..
34181     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34182     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34183         // existingProxy ???
34184     //}
34185     
34186     this.el = Roo.get(cfg.dragElement, true);
34187     this.el.dom.unselectable = "on";
34188     /** @private */
34189     this.resizingEl = Roo.get(cfg.resizingElement, true);
34190
34191     /**
34192      * @private
34193      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34194      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34195      * @type Number
34196      */
34197     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34198     
34199     /**
34200      * The minimum size of the resizing element. (Defaults to 0)
34201      * @type Number
34202      */
34203     this.minSize = 0;
34204     
34205     /**
34206      * The maximum size of the resizing element. (Defaults to 2000)
34207      * @type Number
34208      */
34209     this.maxSize = 2000;
34210     
34211     /**
34212      * Whether to animate the transition to the new size
34213      * @type Boolean
34214      */
34215     this.animate = false;
34216     
34217     /**
34218      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34219      * @type Boolean
34220      */
34221     this.useShim = false;
34222     
34223     /** @private */
34224     this.shim = null;
34225     
34226     if(!cfg.existingProxy){
34227         /** @private */
34228         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34229     }else{
34230         this.proxy = Roo.get(cfg.existingProxy).dom;
34231     }
34232     /** @private */
34233     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34234     
34235     /** @private */
34236     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34237     
34238     /** @private */
34239     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34240     
34241     /** @private */
34242     this.dragSpecs = {};
34243     
34244     /**
34245      * @private The adapter to use to positon and resize elements
34246      */
34247     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34248     this.adapter.init(this);
34249     
34250     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34251         /** @private */
34252         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34253         this.el.addClass("roo-splitbar-h");
34254     }else{
34255         /** @private */
34256         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34257         this.el.addClass("roo-splitbar-v");
34258     }
34259     
34260     this.addEvents({
34261         /**
34262          * @event resize
34263          * Fires when the splitter is moved (alias for {@link #event-moved})
34264          * @param {Roo.bootstrap.SplitBar} this
34265          * @param {Number} newSize the new width or height
34266          */
34267         "resize" : true,
34268         /**
34269          * @event moved
34270          * Fires when the splitter is moved
34271          * @param {Roo.bootstrap.SplitBar} this
34272          * @param {Number} newSize the new width or height
34273          */
34274         "moved" : true,
34275         /**
34276          * @event beforeresize
34277          * Fires before the splitter is dragged
34278          * @param {Roo.bootstrap.SplitBar} this
34279          */
34280         "beforeresize" : true,
34281
34282         "beforeapply" : true
34283     });
34284
34285     Roo.util.Observable.call(this);
34286 };
34287
34288 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34289     onStartProxyDrag : function(x, y){
34290         this.fireEvent("beforeresize", this);
34291         if(!this.overlay){
34292             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34293             o.unselectable();
34294             o.enableDisplayMode("block");
34295             // all splitbars share the same overlay
34296             Roo.bootstrap.SplitBar.prototype.overlay = o;
34297         }
34298         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34299         this.overlay.show();
34300         Roo.get(this.proxy).setDisplayed("block");
34301         var size = this.adapter.getElementSize(this);
34302         this.activeMinSize = this.getMinimumSize();;
34303         this.activeMaxSize = this.getMaximumSize();;
34304         var c1 = size - this.activeMinSize;
34305         var c2 = Math.max(this.activeMaxSize - size, 0);
34306         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34307             this.dd.resetConstraints();
34308             this.dd.setXConstraint(
34309                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34310                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34311             );
34312             this.dd.setYConstraint(0, 0);
34313         }else{
34314             this.dd.resetConstraints();
34315             this.dd.setXConstraint(0, 0);
34316             this.dd.setYConstraint(
34317                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34318                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34319             );
34320          }
34321         this.dragSpecs.startSize = size;
34322         this.dragSpecs.startPoint = [x, y];
34323         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34324     },
34325     
34326     /** 
34327      * @private Called after the drag operation by the DDProxy
34328      */
34329     onEndProxyDrag : function(e){
34330         Roo.get(this.proxy).setDisplayed(false);
34331         var endPoint = Roo.lib.Event.getXY(e);
34332         if(this.overlay){
34333             this.overlay.hide();
34334         }
34335         var newSize;
34336         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34337             newSize = this.dragSpecs.startSize + 
34338                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34339                     endPoint[0] - this.dragSpecs.startPoint[0] :
34340                     this.dragSpecs.startPoint[0] - endPoint[0]
34341                 );
34342         }else{
34343             newSize = this.dragSpecs.startSize + 
34344                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34345                     endPoint[1] - this.dragSpecs.startPoint[1] :
34346                     this.dragSpecs.startPoint[1] - endPoint[1]
34347                 );
34348         }
34349         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34350         if(newSize != this.dragSpecs.startSize){
34351             if(this.fireEvent('beforeapply', this, newSize) !== false){
34352                 this.adapter.setElementSize(this, newSize);
34353                 this.fireEvent("moved", this, newSize);
34354                 this.fireEvent("resize", this, newSize);
34355             }
34356         }
34357     },
34358     
34359     /**
34360      * Get the adapter this SplitBar uses
34361      * @return The adapter object
34362      */
34363     getAdapter : function(){
34364         return this.adapter;
34365     },
34366     
34367     /**
34368      * Set the adapter this SplitBar uses
34369      * @param {Object} adapter A SplitBar adapter object
34370      */
34371     setAdapter : function(adapter){
34372         this.adapter = adapter;
34373         this.adapter.init(this);
34374     },
34375     
34376     /**
34377      * Gets the minimum size for the resizing element
34378      * @return {Number} The minimum size
34379      */
34380     getMinimumSize : function(){
34381         return this.minSize;
34382     },
34383     
34384     /**
34385      * Sets the minimum size for the resizing element
34386      * @param {Number} minSize The minimum size
34387      */
34388     setMinimumSize : function(minSize){
34389         this.minSize = minSize;
34390     },
34391     
34392     /**
34393      * Gets the maximum size for the resizing element
34394      * @return {Number} The maximum size
34395      */
34396     getMaximumSize : function(){
34397         return this.maxSize;
34398     },
34399     
34400     /**
34401      * Sets the maximum size for the resizing element
34402      * @param {Number} maxSize The maximum size
34403      */
34404     setMaximumSize : function(maxSize){
34405         this.maxSize = maxSize;
34406     },
34407     
34408     /**
34409      * Sets the initialize size for the resizing element
34410      * @param {Number} size The initial size
34411      */
34412     setCurrentSize : function(size){
34413         var oldAnimate = this.animate;
34414         this.animate = false;
34415         this.adapter.setElementSize(this, size);
34416         this.animate = oldAnimate;
34417     },
34418     
34419     /**
34420      * Destroy this splitbar. 
34421      * @param {Boolean} removeEl True to remove the element
34422      */
34423     destroy : function(removeEl){
34424         if(this.shim){
34425             this.shim.remove();
34426         }
34427         this.dd.unreg();
34428         this.proxy.parentNode.removeChild(this.proxy);
34429         if(removeEl){
34430             this.el.remove();
34431         }
34432     }
34433 });
34434
34435 /**
34436  * @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.
34437  */
34438 Roo.bootstrap.SplitBar.createProxy = function(dir){
34439     var proxy = new Roo.Element(document.createElement("div"));
34440     proxy.unselectable();
34441     var cls = 'roo-splitbar-proxy';
34442     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34443     document.body.appendChild(proxy.dom);
34444     return proxy.dom;
34445 };
34446
34447 /** 
34448  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34449  * Default Adapter. It assumes the splitter and resizing element are not positioned
34450  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34451  */
34452 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34453 };
34454
34455 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34456     // do nothing for now
34457     init : function(s){
34458     
34459     },
34460     /**
34461      * Called before drag operations to get the current size of the resizing element. 
34462      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34463      */
34464      getElementSize : function(s){
34465         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34466             return s.resizingEl.getWidth();
34467         }else{
34468             return s.resizingEl.getHeight();
34469         }
34470     },
34471     
34472     /**
34473      * Called after drag operations to set the size of the resizing element.
34474      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34475      * @param {Number} newSize The new size to set
34476      * @param {Function} onComplete A function to be invoked when resizing is complete
34477      */
34478     setElementSize : function(s, newSize, onComplete){
34479         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34480             if(!s.animate){
34481                 s.resizingEl.setWidth(newSize);
34482                 if(onComplete){
34483                     onComplete(s, newSize);
34484                 }
34485             }else{
34486                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34487             }
34488         }else{
34489             
34490             if(!s.animate){
34491                 s.resizingEl.setHeight(newSize);
34492                 if(onComplete){
34493                     onComplete(s, newSize);
34494                 }
34495             }else{
34496                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34497             }
34498         }
34499     }
34500 };
34501
34502 /** 
34503  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34504  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34505  * Adapter that  moves the splitter element to align with the resized sizing element. 
34506  * Used with an absolute positioned SplitBar.
34507  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34508  * document.body, make sure you assign an id to the body element.
34509  */
34510 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34511     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34512     this.container = Roo.get(container);
34513 };
34514
34515 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34516     init : function(s){
34517         this.basic.init(s);
34518     },
34519     
34520     getElementSize : function(s){
34521         return this.basic.getElementSize(s);
34522     },
34523     
34524     setElementSize : function(s, newSize, onComplete){
34525         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34526     },
34527     
34528     moveSplitter : function(s){
34529         var yes = Roo.bootstrap.SplitBar;
34530         switch(s.placement){
34531             case yes.LEFT:
34532                 s.el.setX(s.resizingEl.getRight());
34533                 break;
34534             case yes.RIGHT:
34535                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34536                 break;
34537             case yes.TOP:
34538                 s.el.setY(s.resizingEl.getBottom());
34539                 break;
34540             case yes.BOTTOM:
34541                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34542                 break;
34543         }
34544     }
34545 };
34546
34547 /**
34548  * Orientation constant - Create a vertical SplitBar
34549  * @static
34550  * @type Number
34551  */
34552 Roo.bootstrap.SplitBar.VERTICAL = 1;
34553
34554 /**
34555  * Orientation constant - Create a horizontal SplitBar
34556  * @static
34557  * @type Number
34558  */
34559 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34560
34561 /**
34562  * Placement constant - The resizing element is to the left of the splitter element
34563  * @static
34564  * @type Number
34565  */
34566 Roo.bootstrap.SplitBar.LEFT = 1;
34567
34568 /**
34569  * Placement constant - The resizing element is to the right of the splitter element
34570  * @static
34571  * @type Number
34572  */
34573 Roo.bootstrap.SplitBar.RIGHT = 2;
34574
34575 /**
34576  * Placement constant - The resizing element is positioned above the splitter element
34577  * @static
34578  * @type Number
34579  */
34580 Roo.bootstrap.SplitBar.TOP = 3;
34581
34582 /**
34583  * Placement constant - The resizing element is positioned under splitter element
34584  * @static
34585  * @type Number
34586  */
34587 Roo.bootstrap.SplitBar.BOTTOM = 4;
34588 Roo.namespace("Roo.bootstrap.layout");/*
34589  * Based on:
34590  * Ext JS Library 1.1.1
34591  * Copyright(c) 2006-2007, Ext JS, LLC.
34592  *
34593  * Originally Released Under LGPL - original licence link has changed is not relivant.
34594  *
34595  * Fork - LGPL
34596  * <script type="text/javascript">
34597  */
34598
34599 /**
34600  * @class Roo.bootstrap.layout.Manager
34601  * @extends Roo.bootstrap.Component
34602  * Base class for layout managers.
34603  */
34604 Roo.bootstrap.layout.Manager = function(config)
34605 {
34606     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34607
34608
34609
34610
34611
34612     /** false to disable window resize monitoring @type Boolean */
34613     this.monitorWindowResize = true;
34614     this.regions = {};
34615     this.addEvents({
34616         /**
34617          * @event layout
34618          * Fires when a layout is performed.
34619          * @param {Roo.LayoutManager} this
34620          */
34621         "layout" : true,
34622         /**
34623          * @event regionresized
34624          * Fires when the user resizes a region.
34625          * @param {Roo.LayoutRegion} region The resized region
34626          * @param {Number} newSize The new size (width for east/west, height for north/south)
34627          */
34628         "regionresized" : true,
34629         /**
34630          * @event regioncollapsed
34631          * Fires when a region is collapsed.
34632          * @param {Roo.LayoutRegion} region The collapsed region
34633          */
34634         "regioncollapsed" : true,
34635         /**
34636          * @event regionexpanded
34637          * Fires when a region is expanded.
34638          * @param {Roo.LayoutRegion} region The expanded region
34639          */
34640         "regionexpanded" : true
34641     });
34642     this.updating = false;
34643
34644     if (config.el) {
34645         this.el = Roo.get(config.el);
34646         this.initEvents();
34647     }
34648
34649 };
34650
34651 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34652
34653
34654     regions : null,
34655
34656     monitorWindowResize : true,
34657
34658
34659     updating : false,
34660
34661
34662     onRender : function(ct, position)
34663     {
34664         if(!this.el){
34665             this.el = Roo.get(ct);
34666             this.initEvents();
34667         }
34668         //this.fireEvent('render',this);
34669     },
34670
34671
34672     initEvents: function()
34673     {
34674
34675
34676         // ie scrollbar fix
34677         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34678             document.body.scroll = "no";
34679         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34680             this.el.position('relative');
34681         }
34682         this.id = this.el.id;
34683         this.el.addClass("roo-layout-container");
34684         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34685         if(this.el.dom != document.body ) {
34686             this.el.on('resize', this.layout,this);
34687             this.el.on('show', this.layout,this);
34688         }
34689
34690     },
34691
34692     /**
34693      * Returns true if this layout is currently being updated
34694      * @return {Boolean}
34695      */
34696     isUpdating : function(){
34697         return this.updating;
34698     },
34699
34700     /**
34701      * Suspend the LayoutManager from doing auto-layouts while
34702      * making multiple add or remove calls
34703      */
34704     beginUpdate : function(){
34705         this.updating = true;
34706     },
34707
34708     /**
34709      * Restore auto-layouts and optionally disable the manager from performing a layout
34710      * @param {Boolean} noLayout true to disable a layout update
34711      */
34712     endUpdate : function(noLayout){
34713         this.updating = false;
34714         if(!noLayout){
34715             this.layout();
34716         }
34717     },
34718
34719     layout: function(){
34720         // abstract...
34721     },
34722
34723     onRegionResized : function(region, newSize){
34724         this.fireEvent("regionresized", region, newSize);
34725         this.layout();
34726     },
34727
34728     onRegionCollapsed : function(region){
34729         this.fireEvent("regioncollapsed", region);
34730     },
34731
34732     onRegionExpanded : function(region){
34733         this.fireEvent("regionexpanded", region);
34734     },
34735
34736     /**
34737      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34738      * performs box-model adjustments.
34739      * @return {Object} The size as an object {width: (the width), height: (the height)}
34740      */
34741     getViewSize : function()
34742     {
34743         var size;
34744         if(this.el.dom != document.body){
34745             size = this.el.getSize();
34746         }else{
34747             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34748         }
34749         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34750         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34751         return size;
34752     },
34753
34754     /**
34755      * Returns the Element this layout is bound to.
34756      * @return {Roo.Element}
34757      */
34758     getEl : function(){
34759         return this.el;
34760     },
34761
34762     /**
34763      * Returns the specified region.
34764      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34765      * @return {Roo.LayoutRegion}
34766      */
34767     getRegion : function(target){
34768         return this.regions[target.toLowerCase()];
34769     },
34770
34771     onWindowResize : function(){
34772         if(this.monitorWindowResize){
34773             this.layout();
34774         }
34775     }
34776 });
34777 /*
34778  * Based on:
34779  * Ext JS Library 1.1.1
34780  * Copyright(c) 2006-2007, Ext JS, LLC.
34781  *
34782  * Originally Released Under LGPL - original licence link has changed is not relivant.
34783  *
34784  * Fork - LGPL
34785  * <script type="text/javascript">
34786  */
34787 /**
34788  * @class Roo.bootstrap.layout.Border
34789  * @extends Roo.bootstrap.layout.Manager
34790  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34791  * please see: examples/bootstrap/nested.html<br><br>
34792  
34793 <b>The container the layout is rendered into can be either the body element or any other element.
34794 If it is not the body element, the container needs to either be an absolute positioned element,
34795 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34796 the container size if it is not the body element.</b>
34797
34798 * @constructor
34799 * Create a new Border
34800 * @param {Object} config Configuration options
34801  */
34802 Roo.bootstrap.layout.Border = function(config){
34803     config = config || {};
34804     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34805     
34806     
34807     
34808     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34809         if(config[region]){
34810             config[region].region = region;
34811             this.addRegion(config[region]);
34812         }
34813     },this);
34814     
34815 };
34816
34817 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34818
34819 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34820     /**
34821      * Creates and adds a new region if it doesn't already exist.
34822      * @param {String} target The target region key (north, south, east, west or center).
34823      * @param {Object} config The regions config object
34824      * @return {BorderLayoutRegion} The new region
34825      */
34826     addRegion : function(config)
34827     {
34828         if(!this.regions[config.region]){
34829             var r = this.factory(config);
34830             this.bindRegion(r);
34831         }
34832         return this.regions[config.region];
34833     },
34834
34835     // private (kinda)
34836     bindRegion : function(r){
34837         this.regions[r.config.region] = r;
34838         
34839         r.on("visibilitychange",    this.layout, this);
34840         r.on("paneladded",          this.layout, this);
34841         r.on("panelremoved",        this.layout, this);
34842         r.on("invalidated",         this.layout, this);
34843         r.on("resized",             this.onRegionResized, this);
34844         r.on("collapsed",           this.onRegionCollapsed, this);
34845         r.on("expanded",            this.onRegionExpanded, this);
34846     },
34847
34848     /**
34849      * Performs a layout update.
34850      */
34851     layout : function()
34852     {
34853         if(this.updating) {
34854             return;
34855         }
34856         
34857         // render all the rebions if they have not been done alreayd?
34858         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34859             if(this.regions[region] && !this.regions[region].bodyEl){
34860                 this.regions[region].onRender(this.el)
34861             }
34862         },this);
34863         
34864         var size = this.getViewSize();
34865         var w = size.width;
34866         var h = size.height;
34867         var centerW = w;
34868         var centerH = h;
34869         var centerY = 0;
34870         var centerX = 0;
34871         //var x = 0, y = 0;
34872
34873         var rs = this.regions;
34874         var north = rs["north"];
34875         var south = rs["south"]; 
34876         var west = rs["west"];
34877         var east = rs["east"];
34878         var center = rs["center"];
34879         //if(this.hideOnLayout){ // not supported anymore
34880             //c.el.setStyle("display", "none");
34881         //}
34882         if(north && north.isVisible()){
34883             var b = north.getBox();
34884             var m = north.getMargins();
34885             b.width = w - (m.left+m.right);
34886             b.x = m.left;
34887             b.y = m.top;
34888             centerY = b.height + b.y + m.bottom;
34889             centerH -= centerY;
34890             north.updateBox(this.safeBox(b));
34891         }
34892         if(south && south.isVisible()){
34893             var b = south.getBox();
34894             var m = south.getMargins();
34895             b.width = w - (m.left+m.right);
34896             b.x = m.left;
34897             var totalHeight = (b.height + m.top + m.bottom);
34898             b.y = h - totalHeight + m.top;
34899             centerH -= totalHeight;
34900             south.updateBox(this.safeBox(b));
34901         }
34902         if(west && west.isVisible()){
34903             var b = west.getBox();
34904             var m = west.getMargins();
34905             b.height = centerH - (m.top+m.bottom);
34906             b.x = m.left;
34907             b.y = centerY + m.top;
34908             var totalWidth = (b.width + m.left + m.right);
34909             centerX += totalWidth;
34910             centerW -= totalWidth;
34911             west.updateBox(this.safeBox(b));
34912         }
34913         if(east && east.isVisible()){
34914             var b = east.getBox();
34915             var m = east.getMargins();
34916             b.height = centerH - (m.top+m.bottom);
34917             var totalWidth = (b.width + m.left + m.right);
34918             b.x = w - totalWidth + m.left;
34919             b.y = centerY + m.top;
34920             centerW -= totalWidth;
34921             east.updateBox(this.safeBox(b));
34922         }
34923         if(center){
34924             var m = center.getMargins();
34925             var centerBox = {
34926                 x: centerX + m.left,
34927                 y: centerY + m.top,
34928                 width: centerW - (m.left+m.right),
34929                 height: centerH - (m.top+m.bottom)
34930             };
34931             //if(this.hideOnLayout){
34932                 //center.el.setStyle("display", "block");
34933             //}
34934             center.updateBox(this.safeBox(centerBox));
34935         }
34936         this.el.repaint();
34937         this.fireEvent("layout", this);
34938     },
34939
34940     // private
34941     safeBox : function(box){
34942         box.width = Math.max(0, box.width);
34943         box.height = Math.max(0, box.height);
34944         return box;
34945     },
34946
34947     /**
34948      * Adds a ContentPanel (or subclass) to this layout.
34949      * @param {String} target The target region key (north, south, east, west or center).
34950      * @param {Roo.ContentPanel} panel The panel to add
34951      * @return {Roo.ContentPanel} The added panel
34952      */
34953     add : function(target, panel){
34954          
34955         target = target.toLowerCase();
34956         return this.regions[target].add(panel);
34957     },
34958
34959     /**
34960      * Remove a ContentPanel (or subclass) to this layout.
34961      * @param {String} target The target region key (north, south, east, west or center).
34962      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34963      * @return {Roo.ContentPanel} The removed panel
34964      */
34965     remove : function(target, panel){
34966         target = target.toLowerCase();
34967         return this.regions[target].remove(panel);
34968     },
34969
34970     /**
34971      * Searches all regions for a panel with the specified id
34972      * @param {String} panelId
34973      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34974      */
34975     findPanel : function(panelId){
34976         var rs = this.regions;
34977         for(var target in rs){
34978             if(typeof rs[target] != "function"){
34979                 var p = rs[target].getPanel(panelId);
34980                 if(p){
34981                     return p;
34982                 }
34983             }
34984         }
34985         return null;
34986     },
34987
34988     /**
34989      * Searches all regions for a panel with the specified id and activates (shows) it.
34990      * @param {String/ContentPanel} panelId The panels id or the panel itself
34991      * @return {Roo.ContentPanel} The shown panel or null
34992      */
34993     showPanel : function(panelId) {
34994       var rs = this.regions;
34995       for(var target in rs){
34996          var r = rs[target];
34997          if(typeof r != "function"){
34998             if(r.hasPanel(panelId)){
34999                return r.showPanel(panelId);
35000             }
35001          }
35002       }
35003       return null;
35004    },
35005
35006    /**
35007      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35008      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35009      */
35010    /*
35011     restoreState : function(provider){
35012         if(!provider){
35013             provider = Roo.state.Manager;
35014         }
35015         var sm = new Roo.LayoutStateManager();
35016         sm.init(this, provider);
35017     },
35018 */
35019  
35020  
35021     /**
35022      * Adds a xtype elements to the layout.
35023      * <pre><code>
35024
35025 layout.addxtype({
35026        xtype : 'ContentPanel',
35027        region: 'west',
35028        items: [ .... ]
35029    }
35030 );
35031
35032 layout.addxtype({
35033         xtype : 'NestedLayoutPanel',
35034         region: 'west',
35035         layout: {
35036            center: { },
35037            west: { }   
35038         },
35039         items : [ ... list of content panels or nested layout panels.. ]
35040    }
35041 );
35042 </code></pre>
35043      * @param {Object} cfg Xtype definition of item to add.
35044      */
35045     addxtype : function(cfg)
35046     {
35047         // basically accepts a pannel...
35048         // can accept a layout region..!?!?
35049         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35050         
35051         
35052         // theory?  children can only be panels??
35053         
35054         //if (!cfg.xtype.match(/Panel$/)) {
35055         //    return false;
35056         //}
35057         var ret = false;
35058         
35059         if (typeof(cfg.region) == 'undefined') {
35060             Roo.log("Failed to add Panel, region was not set");
35061             Roo.log(cfg);
35062             return false;
35063         }
35064         var region = cfg.region;
35065         delete cfg.region;
35066         
35067           
35068         var xitems = [];
35069         if (cfg.items) {
35070             xitems = cfg.items;
35071             delete cfg.items;
35072         }
35073         var nb = false;
35074         
35075         switch(cfg.xtype) 
35076         {
35077             case 'Content':  // ContentPanel (el, cfg)
35078             case 'Scroll':  // ContentPanel (el, cfg)
35079             case 'View': 
35080                 cfg.autoCreate = true;
35081                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35082                 //} else {
35083                 //    var el = this.el.createChild();
35084                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35085                 //}
35086                 
35087                 this.add(region, ret);
35088                 break;
35089             
35090             /*
35091             case 'TreePanel': // our new panel!
35092                 cfg.el = this.el.createChild();
35093                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35094                 this.add(region, ret);
35095                 break;
35096             */
35097             
35098             case 'Nest': 
35099                 // create a new Layout (which is  a Border Layout...
35100                 
35101                 var clayout = cfg.layout;
35102                 clayout.el  = this.el.createChild();
35103                 clayout.items   = clayout.items  || [];
35104                 
35105                 delete cfg.layout;
35106                 
35107                 // replace this exitems with the clayout ones..
35108                 xitems = clayout.items;
35109                  
35110                 // force background off if it's in center...
35111                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35112                     cfg.background = false;
35113                 }
35114                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35115                 
35116                 
35117                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35118                 //console.log('adding nested layout panel '  + cfg.toSource());
35119                 this.add(region, ret);
35120                 nb = {}; /// find first...
35121                 break;
35122             
35123             case 'Grid':
35124                 
35125                 // needs grid and region
35126                 
35127                 //var el = this.getRegion(region).el.createChild();
35128                 /*
35129                  *var el = this.el.createChild();
35130                 // create the grid first...
35131                 cfg.grid.container = el;
35132                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35133                 */
35134                 
35135                 if (region == 'center' && this.active ) {
35136                     cfg.background = false;
35137                 }
35138                 
35139                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35140                 
35141                 this.add(region, ret);
35142                 /*
35143                 if (cfg.background) {
35144                     // render grid on panel activation (if panel background)
35145                     ret.on('activate', function(gp) {
35146                         if (!gp.grid.rendered) {
35147                     //        gp.grid.render(el);
35148                         }
35149                     });
35150                 } else {
35151                   //  cfg.grid.render(el);
35152                 }
35153                 */
35154                 break;
35155            
35156            
35157             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35158                 // it was the old xcomponent building that caused this before.
35159                 // espeically if border is the top element in the tree.
35160                 ret = this;
35161                 break; 
35162                 
35163                     
35164                 
35165                 
35166                 
35167             default:
35168                 /*
35169                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35170                     
35171                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35172                     this.add(region, ret);
35173                 } else {
35174                 */
35175                     Roo.log(cfg);
35176                     throw "Can not add '" + cfg.xtype + "' to Border";
35177                     return null;
35178              
35179                                 
35180              
35181         }
35182         this.beginUpdate();
35183         // add children..
35184         var region = '';
35185         var abn = {};
35186         Roo.each(xitems, function(i)  {
35187             region = nb && i.region ? i.region : false;
35188             
35189             var add = ret.addxtype(i);
35190            
35191             if (region) {
35192                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35193                 if (!i.background) {
35194                     abn[region] = nb[region] ;
35195                 }
35196             }
35197             
35198         });
35199         this.endUpdate();
35200
35201         // make the last non-background panel active..
35202         //if (nb) { Roo.log(abn); }
35203         if (nb) {
35204             
35205             for(var r in abn) {
35206                 region = this.getRegion(r);
35207                 if (region) {
35208                     // tried using nb[r], but it does not work..
35209                      
35210                     region.showPanel(abn[r]);
35211                    
35212                 }
35213             }
35214         }
35215         return ret;
35216         
35217     },
35218     
35219     
35220 // private
35221     factory : function(cfg)
35222     {
35223         
35224         var validRegions = Roo.bootstrap.layout.Border.regions;
35225
35226         var target = cfg.region;
35227         cfg.mgr = this;
35228         
35229         var r = Roo.bootstrap.layout;
35230         Roo.log(target);
35231         switch(target){
35232             case "north":
35233                 return new r.North(cfg);
35234             case "south":
35235                 return new r.South(cfg);
35236             case "east":
35237                 return new r.East(cfg);
35238             case "west":
35239                 return new r.West(cfg);
35240             case "center":
35241                 return new r.Center(cfg);
35242         }
35243         throw 'Layout region "'+target+'" not supported.';
35244     }
35245     
35246     
35247 });
35248  /*
35249  * Based on:
35250  * Ext JS Library 1.1.1
35251  * Copyright(c) 2006-2007, Ext JS, LLC.
35252  *
35253  * Originally Released Under LGPL - original licence link has changed is not relivant.
35254  *
35255  * Fork - LGPL
35256  * <script type="text/javascript">
35257  */
35258  
35259 /**
35260  * @class Roo.bootstrap.layout.Basic
35261  * @extends Roo.util.Observable
35262  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35263  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35264  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35265  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35266  * @cfg {string}   region  the region that it inhabits..
35267  * @cfg {bool}   skipConfig skip config?
35268  * 
35269
35270  */
35271 Roo.bootstrap.layout.Basic = function(config){
35272     
35273     this.mgr = config.mgr;
35274     
35275     this.position = config.region;
35276     
35277     var skipConfig = config.skipConfig;
35278     
35279     this.events = {
35280         /**
35281          * @scope Roo.BasicLayoutRegion
35282          */
35283         
35284         /**
35285          * @event beforeremove
35286          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35287          * @param {Roo.LayoutRegion} this
35288          * @param {Roo.ContentPanel} panel The panel
35289          * @param {Object} e The cancel event object
35290          */
35291         "beforeremove" : true,
35292         /**
35293          * @event invalidated
35294          * Fires when the layout for this region is changed.
35295          * @param {Roo.LayoutRegion} this
35296          */
35297         "invalidated" : true,
35298         /**
35299          * @event visibilitychange
35300          * Fires when this region is shown or hidden 
35301          * @param {Roo.LayoutRegion} this
35302          * @param {Boolean} visibility true or false
35303          */
35304         "visibilitychange" : true,
35305         /**
35306          * @event paneladded
35307          * Fires when a panel is added. 
35308          * @param {Roo.LayoutRegion} this
35309          * @param {Roo.ContentPanel} panel The panel
35310          */
35311         "paneladded" : true,
35312         /**
35313          * @event panelremoved
35314          * Fires when a panel is removed. 
35315          * @param {Roo.LayoutRegion} this
35316          * @param {Roo.ContentPanel} panel The panel
35317          */
35318         "panelremoved" : true,
35319         /**
35320          * @event beforecollapse
35321          * Fires when this region before collapse.
35322          * @param {Roo.LayoutRegion} this
35323          */
35324         "beforecollapse" : true,
35325         /**
35326          * @event collapsed
35327          * Fires when this region is collapsed.
35328          * @param {Roo.LayoutRegion} this
35329          */
35330         "collapsed" : true,
35331         /**
35332          * @event expanded
35333          * Fires when this region is expanded.
35334          * @param {Roo.LayoutRegion} this
35335          */
35336         "expanded" : true,
35337         /**
35338          * @event slideshow
35339          * Fires when this region is slid into view.
35340          * @param {Roo.LayoutRegion} this
35341          */
35342         "slideshow" : true,
35343         /**
35344          * @event slidehide
35345          * Fires when this region slides out of view. 
35346          * @param {Roo.LayoutRegion} this
35347          */
35348         "slidehide" : true,
35349         /**
35350          * @event panelactivated
35351          * Fires when a panel is activated. 
35352          * @param {Roo.LayoutRegion} this
35353          * @param {Roo.ContentPanel} panel The activated panel
35354          */
35355         "panelactivated" : true,
35356         /**
35357          * @event resized
35358          * Fires when the user resizes this region. 
35359          * @param {Roo.LayoutRegion} this
35360          * @param {Number} newSize The new size (width for east/west, height for north/south)
35361          */
35362         "resized" : true
35363     };
35364     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35365     this.panels = new Roo.util.MixedCollection();
35366     this.panels.getKey = this.getPanelId.createDelegate(this);
35367     this.box = null;
35368     this.activePanel = null;
35369     // ensure listeners are added...
35370     
35371     if (config.listeners || config.events) {
35372         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35373             listeners : config.listeners || {},
35374             events : config.events || {}
35375         });
35376     }
35377     
35378     if(skipConfig !== true){
35379         this.applyConfig(config);
35380     }
35381 };
35382
35383 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35384 {
35385     getPanelId : function(p){
35386         return p.getId();
35387     },
35388     
35389     applyConfig : function(config){
35390         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35391         this.config = config;
35392         
35393     },
35394     
35395     /**
35396      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35397      * the width, for horizontal (north, south) the height.
35398      * @param {Number} newSize The new width or height
35399      */
35400     resizeTo : function(newSize){
35401         var el = this.el ? this.el :
35402                  (this.activePanel ? this.activePanel.getEl() : null);
35403         if(el){
35404             switch(this.position){
35405                 case "east":
35406                 case "west":
35407                     el.setWidth(newSize);
35408                     this.fireEvent("resized", this, newSize);
35409                 break;
35410                 case "north":
35411                 case "south":
35412                     el.setHeight(newSize);
35413                     this.fireEvent("resized", this, newSize);
35414                 break;                
35415             }
35416         }
35417     },
35418     
35419     getBox : function(){
35420         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35421     },
35422     
35423     getMargins : function(){
35424         return this.margins;
35425     },
35426     
35427     updateBox : function(box){
35428         this.box = box;
35429         var el = this.activePanel.getEl();
35430         el.dom.style.left = box.x + "px";
35431         el.dom.style.top = box.y + "px";
35432         this.activePanel.setSize(box.width, box.height);
35433     },
35434     
35435     /**
35436      * Returns the container element for this region.
35437      * @return {Roo.Element}
35438      */
35439     getEl : function(){
35440         return this.activePanel;
35441     },
35442     
35443     /**
35444      * Returns true if this region is currently visible.
35445      * @return {Boolean}
35446      */
35447     isVisible : function(){
35448         return this.activePanel ? true : false;
35449     },
35450     
35451     setActivePanel : function(panel){
35452         panel = this.getPanel(panel);
35453         if(this.activePanel && this.activePanel != panel){
35454             this.activePanel.setActiveState(false);
35455             this.activePanel.getEl().setLeftTop(-10000,-10000);
35456         }
35457         this.activePanel = panel;
35458         panel.setActiveState(true);
35459         if(this.box){
35460             panel.setSize(this.box.width, this.box.height);
35461         }
35462         this.fireEvent("panelactivated", this, panel);
35463         this.fireEvent("invalidated");
35464     },
35465     
35466     /**
35467      * Show the specified panel.
35468      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35469      * @return {Roo.ContentPanel} The shown panel or null
35470      */
35471     showPanel : function(panel){
35472         panel = this.getPanel(panel);
35473         if(panel){
35474             this.setActivePanel(panel);
35475         }
35476         return panel;
35477     },
35478     
35479     /**
35480      * Get the active panel for this region.
35481      * @return {Roo.ContentPanel} The active panel or null
35482      */
35483     getActivePanel : function(){
35484         return this.activePanel;
35485     },
35486     
35487     /**
35488      * Add the passed ContentPanel(s)
35489      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35490      * @return {Roo.ContentPanel} The panel added (if only one was added)
35491      */
35492     add : function(panel){
35493         if(arguments.length > 1){
35494             for(var i = 0, len = arguments.length; i < len; i++) {
35495                 this.add(arguments[i]);
35496             }
35497             return null;
35498         }
35499         if(this.hasPanel(panel)){
35500             this.showPanel(panel);
35501             return panel;
35502         }
35503         var el = panel.getEl();
35504         if(el.dom.parentNode != this.mgr.el.dom){
35505             this.mgr.el.dom.appendChild(el.dom);
35506         }
35507         if(panel.setRegion){
35508             panel.setRegion(this);
35509         }
35510         this.panels.add(panel);
35511         el.setStyle("position", "absolute");
35512         if(!panel.background){
35513             this.setActivePanel(panel);
35514             if(this.config.initialSize && this.panels.getCount()==1){
35515                 this.resizeTo(this.config.initialSize);
35516             }
35517         }
35518         this.fireEvent("paneladded", this, panel);
35519         return panel;
35520     },
35521     
35522     /**
35523      * Returns true if the panel is in this region.
35524      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35525      * @return {Boolean}
35526      */
35527     hasPanel : function(panel){
35528         if(typeof panel == "object"){ // must be panel obj
35529             panel = panel.getId();
35530         }
35531         return this.getPanel(panel) ? true : false;
35532     },
35533     
35534     /**
35535      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35536      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35537      * @param {Boolean} preservePanel Overrides the config preservePanel option
35538      * @return {Roo.ContentPanel} The panel that was removed
35539      */
35540     remove : function(panel, preservePanel){
35541         panel = this.getPanel(panel);
35542         if(!panel){
35543             return null;
35544         }
35545         var e = {};
35546         this.fireEvent("beforeremove", this, panel, e);
35547         if(e.cancel === true){
35548             return null;
35549         }
35550         var panelId = panel.getId();
35551         this.panels.removeKey(panelId);
35552         return panel;
35553     },
35554     
35555     /**
35556      * Returns the panel specified or null if it's not in this region.
35557      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35558      * @return {Roo.ContentPanel}
35559      */
35560     getPanel : function(id){
35561         if(typeof id == "object"){ // must be panel obj
35562             return id;
35563         }
35564         return this.panels.get(id);
35565     },
35566     
35567     /**
35568      * Returns this regions position (north/south/east/west/center).
35569      * @return {String} 
35570      */
35571     getPosition: function(){
35572         return this.position;    
35573     }
35574 });/*
35575  * Based on:
35576  * Ext JS Library 1.1.1
35577  * Copyright(c) 2006-2007, Ext JS, LLC.
35578  *
35579  * Originally Released Under LGPL - original licence link has changed is not relivant.
35580  *
35581  * Fork - LGPL
35582  * <script type="text/javascript">
35583  */
35584  
35585 /**
35586  * @class Roo.bootstrap.layout.Region
35587  * @extends Roo.bootstrap.layout.Basic
35588  * This class represents a region in a layout manager.
35589  
35590  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35591  * @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})
35592  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35593  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35594  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35595  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35596  * @cfg {String}    title           The title for the region (overrides panel titles)
35597  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35598  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35599  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35600  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35601  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35602  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35603  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35604  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35605  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35606  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35607
35608  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35609  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35610  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35611  * @cfg {Number}    width           For East/West panels
35612  * @cfg {Number}    height          For North/South panels
35613  * @cfg {Boolean}   split           To show the splitter
35614  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35615  * 
35616  * @cfg {string}   cls             Extra CSS classes to add to region
35617  * 
35618  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35619  * @cfg {string}   region  the region that it inhabits..
35620  *
35621
35622  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35623  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35624
35625  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35626  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35627  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35628  */
35629 Roo.bootstrap.layout.Region = function(config)
35630 {
35631     this.applyConfig(config);
35632
35633     var mgr = config.mgr;
35634     var pos = config.region;
35635     config.skipConfig = true;
35636     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35637     
35638     if (mgr.el) {
35639         this.onRender(mgr.el);   
35640     }
35641      
35642     this.visible = true;
35643     this.collapsed = false;
35644     this.unrendered_panels = [];
35645 };
35646
35647 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35648
35649     position: '', // set by wrapper (eg. north/south etc..)
35650     unrendered_panels : null,  // unrendered panels.
35651     createBody : function(){
35652         /** This region's body element 
35653         * @type Roo.Element */
35654         this.bodyEl = this.el.createChild({
35655                 tag: "div",
35656                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35657         });
35658     },
35659
35660     onRender: function(ctr, pos)
35661     {
35662         var dh = Roo.DomHelper;
35663         /** This region's container element 
35664         * @type Roo.Element */
35665         this.el = dh.append(ctr.dom, {
35666                 tag: "div",
35667                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35668             }, true);
35669         /** This region's title element 
35670         * @type Roo.Element */
35671     
35672         this.titleEl = dh.append(this.el.dom,
35673             {
35674                     tag: "div",
35675                     unselectable: "on",
35676                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35677                     children:[
35678                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35679                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35680                     ]}, true);
35681         
35682         this.titleEl.enableDisplayMode();
35683         /** This region's title text element 
35684         * @type HTMLElement */
35685         this.titleTextEl = this.titleEl.dom.firstChild;
35686         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35687         /*
35688         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35689         this.closeBtn.enableDisplayMode();
35690         this.closeBtn.on("click", this.closeClicked, this);
35691         this.closeBtn.hide();
35692     */
35693         this.createBody(this.config);
35694         if(this.config.hideWhenEmpty){
35695             this.hide();
35696             this.on("paneladded", this.validateVisibility, this);
35697             this.on("panelremoved", this.validateVisibility, this);
35698         }
35699         if(this.autoScroll){
35700             this.bodyEl.setStyle("overflow", "auto");
35701         }else{
35702             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35703         }
35704         //if(c.titlebar !== false){
35705             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35706                 this.titleEl.hide();
35707             }else{
35708                 this.titleEl.show();
35709                 if(this.config.title){
35710                     this.titleTextEl.innerHTML = this.config.title;
35711                 }
35712             }
35713         //}
35714         if(this.config.collapsed){
35715             this.collapse(true);
35716         }
35717         if(this.config.hidden){
35718             this.hide();
35719         }
35720         
35721         if (this.unrendered_panels && this.unrendered_panels.length) {
35722             for (var i =0;i< this.unrendered_panels.length; i++) {
35723                 this.add(this.unrendered_panels[i]);
35724             }
35725             this.unrendered_panels = null;
35726             
35727         }
35728         
35729     },
35730     
35731     applyConfig : function(c)
35732     {
35733         /*
35734          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35735             var dh = Roo.DomHelper;
35736             if(c.titlebar !== false){
35737                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35738                 this.collapseBtn.on("click", this.collapse, this);
35739                 this.collapseBtn.enableDisplayMode();
35740                 /*
35741                 if(c.showPin === true || this.showPin){
35742                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35743                     this.stickBtn.enableDisplayMode();
35744                     this.stickBtn.on("click", this.expand, this);
35745                     this.stickBtn.hide();
35746                 }
35747                 
35748             }
35749             */
35750             /** This region's collapsed element
35751             * @type Roo.Element */
35752             /*
35753              *
35754             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35755                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35756             ]}, true);
35757             
35758             if(c.floatable !== false){
35759                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35760                this.collapsedEl.on("click", this.collapseClick, this);
35761             }
35762
35763             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35764                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35765                    id: "message", unselectable: "on", style:{"float":"left"}});
35766                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35767              }
35768             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35769             this.expandBtn.on("click", this.expand, this);
35770             
35771         }
35772         
35773         if(this.collapseBtn){
35774             this.collapseBtn.setVisible(c.collapsible == true);
35775         }
35776         
35777         this.cmargins = c.cmargins || this.cmargins ||
35778                          (this.position == "west" || this.position == "east" ?
35779                              {top: 0, left: 2, right:2, bottom: 0} :
35780                              {top: 2, left: 0, right:0, bottom: 2});
35781         */
35782         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35783         
35784         
35785         this.bottomTabs = c.tabPosition != "top";
35786         
35787         this.autoScroll = c.autoScroll || false;
35788         
35789         
35790        
35791         
35792         this.duration = c.duration || .30;
35793         this.slideDuration = c.slideDuration || .45;
35794         this.config = c;
35795        
35796     },
35797     /**
35798      * Returns true if this region is currently visible.
35799      * @return {Boolean}
35800      */
35801     isVisible : function(){
35802         return this.visible;
35803     },
35804
35805     /**
35806      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35807      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35808      */
35809     //setCollapsedTitle : function(title){
35810     //    title = title || "&#160;";
35811      //   if(this.collapsedTitleTextEl){
35812       //      this.collapsedTitleTextEl.innerHTML = title;
35813        // }
35814     //},
35815
35816     getBox : function(){
35817         var b;
35818       //  if(!this.collapsed){
35819             b = this.el.getBox(false, true);
35820        // }else{
35821           //  b = this.collapsedEl.getBox(false, true);
35822         //}
35823         return b;
35824     },
35825
35826     getMargins : function(){
35827         return this.margins;
35828         //return this.collapsed ? this.cmargins : this.margins;
35829     },
35830 /*
35831     highlight : function(){
35832         this.el.addClass("x-layout-panel-dragover");
35833     },
35834
35835     unhighlight : function(){
35836         this.el.removeClass("x-layout-panel-dragover");
35837     },
35838 */
35839     updateBox : function(box)
35840     {
35841         if (!this.bodyEl) {
35842             return; // not rendered yet..
35843         }
35844         
35845         this.box = box;
35846         if(!this.collapsed){
35847             this.el.dom.style.left = box.x + "px";
35848             this.el.dom.style.top = box.y + "px";
35849             this.updateBody(box.width, box.height);
35850         }else{
35851             this.collapsedEl.dom.style.left = box.x + "px";
35852             this.collapsedEl.dom.style.top = box.y + "px";
35853             this.collapsedEl.setSize(box.width, box.height);
35854         }
35855         if(this.tabs){
35856             this.tabs.autoSizeTabs();
35857         }
35858     },
35859
35860     updateBody : function(w, h)
35861     {
35862         if(w !== null){
35863             this.el.setWidth(w);
35864             w -= this.el.getBorderWidth("rl");
35865             if(this.config.adjustments){
35866                 w += this.config.adjustments[0];
35867             }
35868         }
35869         if(h !== null && h > 0){
35870             this.el.setHeight(h);
35871             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35872             h -= this.el.getBorderWidth("tb");
35873             if(this.config.adjustments){
35874                 h += this.config.adjustments[1];
35875             }
35876             this.bodyEl.setHeight(h);
35877             if(this.tabs){
35878                 h = this.tabs.syncHeight(h);
35879             }
35880         }
35881         if(this.panelSize){
35882             w = w !== null ? w : this.panelSize.width;
35883             h = h !== null ? h : this.panelSize.height;
35884         }
35885         if(this.activePanel){
35886             var el = this.activePanel.getEl();
35887             w = w !== null ? w : el.getWidth();
35888             h = h !== null ? h : el.getHeight();
35889             this.panelSize = {width: w, height: h};
35890             this.activePanel.setSize(w, h);
35891         }
35892         if(Roo.isIE && this.tabs){
35893             this.tabs.el.repaint();
35894         }
35895     },
35896
35897     /**
35898      * Returns the container element for this region.
35899      * @return {Roo.Element}
35900      */
35901     getEl : function(){
35902         return this.el;
35903     },
35904
35905     /**
35906      * Hides this region.
35907      */
35908     hide : function(){
35909         //if(!this.collapsed){
35910             this.el.dom.style.left = "-2000px";
35911             this.el.hide();
35912         //}else{
35913          //   this.collapsedEl.dom.style.left = "-2000px";
35914          //   this.collapsedEl.hide();
35915        // }
35916         this.visible = false;
35917         this.fireEvent("visibilitychange", this, false);
35918     },
35919
35920     /**
35921      * Shows this region if it was previously hidden.
35922      */
35923     show : function(){
35924         //if(!this.collapsed){
35925             this.el.show();
35926         //}else{
35927         //    this.collapsedEl.show();
35928        // }
35929         this.visible = true;
35930         this.fireEvent("visibilitychange", this, true);
35931     },
35932 /*
35933     closeClicked : function(){
35934         if(this.activePanel){
35935             this.remove(this.activePanel);
35936         }
35937     },
35938
35939     collapseClick : function(e){
35940         if(this.isSlid){
35941            e.stopPropagation();
35942            this.slideIn();
35943         }else{
35944            e.stopPropagation();
35945            this.slideOut();
35946         }
35947     },
35948 */
35949     /**
35950      * Collapses this region.
35951      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35952      */
35953     /*
35954     collapse : function(skipAnim, skipCheck = false){
35955         if(this.collapsed) {
35956             return;
35957         }
35958         
35959         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35960             
35961             this.collapsed = true;
35962             if(this.split){
35963                 this.split.el.hide();
35964             }
35965             if(this.config.animate && skipAnim !== true){
35966                 this.fireEvent("invalidated", this);
35967                 this.animateCollapse();
35968             }else{
35969                 this.el.setLocation(-20000,-20000);
35970                 this.el.hide();
35971                 this.collapsedEl.show();
35972                 this.fireEvent("collapsed", this);
35973                 this.fireEvent("invalidated", this);
35974             }
35975         }
35976         
35977     },
35978 */
35979     animateCollapse : function(){
35980         // overridden
35981     },
35982
35983     /**
35984      * Expands this region if it was previously collapsed.
35985      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35986      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35987      */
35988     /*
35989     expand : function(e, skipAnim){
35990         if(e) {
35991             e.stopPropagation();
35992         }
35993         if(!this.collapsed || this.el.hasActiveFx()) {
35994             return;
35995         }
35996         if(this.isSlid){
35997             this.afterSlideIn();
35998             skipAnim = true;
35999         }
36000         this.collapsed = false;
36001         if(this.config.animate && skipAnim !== true){
36002             this.animateExpand();
36003         }else{
36004             this.el.show();
36005             if(this.split){
36006                 this.split.el.show();
36007             }
36008             this.collapsedEl.setLocation(-2000,-2000);
36009             this.collapsedEl.hide();
36010             this.fireEvent("invalidated", this);
36011             this.fireEvent("expanded", this);
36012         }
36013     },
36014 */
36015     animateExpand : function(){
36016         // overridden
36017     },
36018
36019     initTabs : function()
36020     {
36021         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36022         
36023         var ts = new Roo.bootstrap.panel.Tabs({
36024                 el: this.bodyEl.dom,
36025                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36026                 disableTooltips: this.config.disableTabTips,
36027                 toolbar : this.config.toolbar
36028             });
36029         
36030         if(this.config.hideTabs){
36031             ts.stripWrap.setDisplayed(false);
36032         }
36033         this.tabs = ts;
36034         ts.resizeTabs = this.config.resizeTabs === true;
36035         ts.minTabWidth = this.config.minTabWidth || 40;
36036         ts.maxTabWidth = this.config.maxTabWidth || 250;
36037         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36038         ts.monitorResize = false;
36039         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36040         ts.bodyEl.addClass('roo-layout-tabs-body');
36041         this.panels.each(this.initPanelAsTab, this);
36042     },
36043
36044     initPanelAsTab : function(panel){
36045         var ti = this.tabs.addTab(
36046             panel.getEl().id,
36047             panel.getTitle(),
36048             null,
36049             this.config.closeOnTab && panel.isClosable(),
36050             panel.tpl
36051         );
36052         if(panel.tabTip !== undefined){
36053             ti.setTooltip(panel.tabTip);
36054         }
36055         ti.on("activate", function(){
36056               this.setActivePanel(panel);
36057         }, this);
36058         
36059         if(this.config.closeOnTab){
36060             ti.on("beforeclose", function(t, e){
36061                 e.cancel = true;
36062                 this.remove(panel);
36063             }, this);
36064         }
36065         
36066         panel.tabItem = ti;
36067         
36068         return ti;
36069     },
36070
36071     updatePanelTitle : function(panel, title)
36072     {
36073         if(this.activePanel == panel){
36074             this.updateTitle(title);
36075         }
36076         if(this.tabs){
36077             var ti = this.tabs.getTab(panel.getEl().id);
36078             ti.setText(title);
36079             if(panel.tabTip !== undefined){
36080                 ti.setTooltip(panel.tabTip);
36081             }
36082         }
36083     },
36084
36085     updateTitle : function(title){
36086         if(this.titleTextEl && !this.config.title){
36087             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36088         }
36089     },
36090
36091     setActivePanel : function(panel)
36092     {
36093         panel = this.getPanel(panel);
36094         if(this.activePanel && this.activePanel != panel){
36095             if(this.activePanel.setActiveState(false) === false){
36096                 return;
36097             }
36098         }
36099         this.activePanel = panel;
36100         panel.setActiveState(true);
36101         if(this.panelSize){
36102             panel.setSize(this.panelSize.width, this.panelSize.height);
36103         }
36104         if(this.closeBtn){
36105             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36106         }
36107         this.updateTitle(panel.getTitle());
36108         if(this.tabs){
36109             this.fireEvent("invalidated", this);
36110         }
36111         this.fireEvent("panelactivated", this, panel);
36112     },
36113
36114     /**
36115      * Shows the specified panel.
36116      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36117      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36118      */
36119     showPanel : function(panel)
36120     {
36121         panel = this.getPanel(panel);
36122         if(panel){
36123             if(this.tabs){
36124                 var tab = this.tabs.getTab(panel.getEl().id);
36125                 if(tab.isHidden()){
36126                     this.tabs.unhideTab(tab.id);
36127                 }
36128                 tab.activate();
36129             }else{
36130                 this.setActivePanel(panel);
36131             }
36132         }
36133         return panel;
36134     },
36135
36136     /**
36137      * Get the active panel for this region.
36138      * @return {Roo.ContentPanel} The active panel or null
36139      */
36140     getActivePanel : function(){
36141         return this.activePanel;
36142     },
36143
36144     validateVisibility : function(){
36145         if(this.panels.getCount() < 1){
36146             this.updateTitle("&#160;");
36147             this.closeBtn.hide();
36148             this.hide();
36149         }else{
36150             if(!this.isVisible()){
36151                 this.show();
36152             }
36153         }
36154     },
36155
36156     /**
36157      * Adds the passed ContentPanel(s) to this region.
36158      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36159      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36160      */
36161     add : function(panel)
36162     {
36163         if(arguments.length > 1){
36164             for(var i = 0, len = arguments.length; i < len; i++) {
36165                 this.add(arguments[i]);
36166             }
36167             return null;
36168         }
36169         
36170         // if we have not been rendered yet, then we can not really do much of this..
36171         if (!this.bodyEl) {
36172             this.unrendered_panels.push(panel);
36173             return panel;
36174         }
36175         
36176         
36177         
36178         
36179         if(this.hasPanel(panel)){
36180             this.showPanel(panel);
36181             return panel;
36182         }
36183         panel.setRegion(this);
36184         this.panels.add(panel);
36185        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36186             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36187             // and hide them... ???
36188             this.bodyEl.dom.appendChild(panel.getEl().dom);
36189             if(panel.background !== true){
36190                 this.setActivePanel(panel);
36191             }
36192             this.fireEvent("paneladded", this, panel);
36193             return panel;
36194         }
36195         */
36196         if(!this.tabs){
36197             this.initTabs();
36198         }else{
36199             this.initPanelAsTab(panel);
36200         }
36201         
36202         
36203         if(panel.background !== true){
36204             this.tabs.activate(panel.getEl().id);
36205         }
36206         this.fireEvent("paneladded", this, panel);
36207         return panel;
36208     },
36209
36210     /**
36211      * Hides the tab for the specified panel.
36212      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36213      */
36214     hidePanel : function(panel){
36215         if(this.tabs && (panel = this.getPanel(panel))){
36216             this.tabs.hideTab(panel.getEl().id);
36217         }
36218     },
36219
36220     /**
36221      * Unhides the tab for a previously hidden panel.
36222      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36223      */
36224     unhidePanel : function(panel){
36225         if(this.tabs && (panel = this.getPanel(panel))){
36226             this.tabs.unhideTab(panel.getEl().id);
36227         }
36228     },
36229
36230     clearPanels : function(){
36231         while(this.panels.getCount() > 0){
36232              this.remove(this.panels.first());
36233         }
36234     },
36235
36236     /**
36237      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36238      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36239      * @param {Boolean} preservePanel Overrides the config preservePanel option
36240      * @return {Roo.ContentPanel} The panel that was removed
36241      */
36242     remove : function(panel, preservePanel)
36243     {
36244         panel = this.getPanel(panel);
36245         if(!panel){
36246             return null;
36247         }
36248         var e = {};
36249         this.fireEvent("beforeremove", this, panel, e);
36250         if(e.cancel === true){
36251             return null;
36252         }
36253         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36254         var panelId = panel.getId();
36255         this.panels.removeKey(panelId);
36256         if(preservePanel){
36257             document.body.appendChild(panel.getEl().dom);
36258         }
36259         if(this.tabs){
36260             this.tabs.removeTab(panel.getEl().id);
36261         }else if (!preservePanel){
36262             this.bodyEl.dom.removeChild(panel.getEl().dom);
36263         }
36264         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36265             var p = this.panels.first();
36266             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36267             tempEl.appendChild(p.getEl().dom);
36268             this.bodyEl.update("");
36269             this.bodyEl.dom.appendChild(p.getEl().dom);
36270             tempEl = null;
36271             this.updateTitle(p.getTitle());
36272             this.tabs = null;
36273             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36274             this.setActivePanel(p);
36275         }
36276         panel.setRegion(null);
36277         if(this.activePanel == panel){
36278             this.activePanel = null;
36279         }
36280         if(this.config.autoDestroy !== false && preservePanel !== true){
36281             try{panel.destroy();}catch(e){}
36282         }
36283         this.fireEvent("panelremoved", this, panel);
36284         return panel;
36285     },
36286
36287     /**
36288      * Returns the TabPanel component used by this region
36289      * @return {Roo.TabPanel}
36290      */
36291     getTabs : function(){
36292         return this.tabs;
36293     },
36294
36295     createTool : function(parentEl, className){
36296         var btn = Roo.DomHelper.append(parentEl, {
36297             tag: "div",
36298             cls: "x-layout-tools-button",
36299             children: [ {
36300                 tag: "div",
36301                 cls: "roo-layout-tools-button-inner " + className,
36302                 html: "&#160;"
36303             }]
36304         }, true);
36305         btn.addClassOnOver("roo-layout-tools-button-over");
36306         return btn;
36307     }
36308 });/*
36309  * Based on:
36310  * Ext JS Library 1.1.1
36311  * Copyright(c) 2006-2007, Ext JS, LLC.
36312  *
36313  * Originally Released Under LGPL - original licence link has changed is not relivant.
36314  *
36315  * Fork - LGPL
36316  * <script type="text/javascript">
36317  */
36318  
36319
36320
36321 /**
36322  * @class Roo.SplitLayoutRegion
36323  * @extends Roo.LayoutRegion
36324  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36325  */
36326 Roo.bootstrap.layout.Split = function(config){
36327     this.cursor = config.cursor;
36328     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36329 };
36330
36331 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36332 {
36333     splitTip : "Drag to resize.",
36334     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36335     useSplitTips : false,
36336
36337     applyConfig : function(config){
36338         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36339     },
36340     
36341     onRender : function(ctr,pos) {
36342         
36343         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36344         if(!this.config.split){
36345             return;
36346         }
36347         if(!this.split){
36348             
36349             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36350                             tag: "div",
36351                             id: this.el.id + "-split",
36352                             cls: "roo-layout-split roo-layout-split-"+this.position,
36353                             html: "&#160;"
36354             });
36355             /** The SplitBar for this region 
36356             * @type Roo.SplitBar */
36357             // does not exist yet...
36358             Roo.log([this.position, this.orientation]);
36359             
36360             this.split = new Roo.bootstrap.SplitBar({
36361                 dragElement : splitEl,
36362                 resizingElement: this.el,
36363                 orientation : this.orientation
36364             });
36365             
36366             this.split.on("moved", this.onSplitMove, this);
36367             this.split.useShim = this.config.useShim === true;
36368             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36369             if(this.useSplitTips){
36370                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36371             }
36372             //if(config.collapsible){
36373             //    this.split.el.on("dblclick", this.collapse,  this);
36374             //}
36375         }
36376         if(typeof this.config.minSize != "undefined"){
36377             this.split.minSize = this.config.minSize;
36378         }
36379         if(typeof this.config.maxSize != "undefined"){
36380             this.split.maxSize = this.config.maxSize;
36381         }
36382         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36383             this.hideSplitter();
36384         }
36385         
36386     },
36387
36388     getHMaxSize : function(){
36389          var cmax = this.config.maxSize || 10000;
36390          var center = this.mgr.getRegion("center");
36391          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36392     },
36393
36394     getVMaxSize : function(){
36395          var cmax = this.config.maxSize || 10000;
36396          var center = this.mgr.getRegion("center");
36397          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36398     },
36399
36400     onSplitMove : function(split, newSize){
36401         this.fireEvent("resized", this, newSize);
36402     },
36403     
36404     /** 
36405      * Returns the {@link Roo.SplitBar} for this region.
36406      * @return {Roo.SplitBar}
36407      */
36408     getSplitBar : function(){
36409         return this.split;
36410     },
36411     
36412     hide : function(){
36413         this.hideSplitter();
36414         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36415     },
36416
36417     hideSplitter : function(){
36418         if(this.split){
36419             this.split.el.setLocation(-2000,-2000);
36420             this.split.el.hide();
36421         }
36422     },
36423
36424     show : function(){
36425         if(this.split){
36426             this.split.el.show();
36427         }
36428         Roo.bootstrap.layout.Split.superclass.show.call(this);
36429     },
36430     
36431     beforeSlide: function(){
36432         if(Roo.isGecko){// firefox overflow auto bug workaround
36433             this.bodyEl.clip();
36434             if(this.tabs) {
36435                 this.tabs.bodyEl.clip();
36436             }
36437             if(this.activePanel){
36438                 this.activePanel.getEl().clip();
36439                 
36440                 if(this.activePanel.beforeSlide){
36441                     this.activePanel.beforeSlide();
36442                 }
36443             }
36444         }
36445     },
36446     
36447     afterSlide : function(){
36448         if(Roo.isGecko){// firefox overflow auto bug workaround
36449             this.bodyEl.unclip();
36450             if(this.tabs) {
36451                 this.tabs.bodyEl.unclip();
36452             }
36453             if(this.activePanel){
36454                 this.activePanel.getEl().unclip();
36455                 if(this.activePanel.afterSlide){
36456                     this.activePanel.afterSlide();
36457                 }
36458             }
36459         }
36460     },
36461
36462     initAutoHide : function(){
36463         if(this.autoHide !== false){
36464             if(!this.autoHideHd){
36465                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36466                 this.autoHideHd = {
36467                     "mouseout": function(e){
36468                         if(!e.within(this.el, true)){
36469                             st.delay(500);
36470                         }
36471                     },
36472                     "mouseover" : function(e){
36473                         st.cancel();
36474                     },
36475                     scope : this
36476                 };
36477             }
36478             this.el.on(this.autoHideHd);
36479         }
36480     },
36481
36482     clearAutoHide : function(){
36483         if(this.autoHide !== false){
36484             this.el.un("mouseout", this.autoHideHd.mouseout);
36485             this.el.un("mouseover", this.autoHideHd.mouseover);
36486         }
36487     },
36488
36489     clearMonitor : function(){
36490         Roo.get(document).un("click", this.slideInIf, this);
36491     },
36492
36493     // these names are backwards but not changed for compat
36494     slideOut : function(){
36495         if(this.isSlid || this.el.hasActiveFx()){
36496             return;
36497         }
36498         this.isSlid = true;
36499         if(this.collapseBtn){
36500             this.collapseBtn.hide();
36501         }
36502         this.closeBtnState = this.closeBtn.getStyle('display');
36503         this.closeBtn.hide();
36504         if(this.stickBtn){
36505             this.stickBtn.show();
36506         }
36507         this.el.show();
36508         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36509         this.beforeSlide();
36510         this.el.setStyle("z-index", 10001);
36511         this.el.slideIn(this.getSlideAnchor(), {
36512             callback: function(){
36513                 this.afterSlide();
36514                 this.initAutoHide();
36515                 Roo.get(document).on("click", this.slideInIf, this);
36516                 this.fireEvent("slideshow", this);
36517             },
36518             scope: this,
36519             block: true
36520         });
36521     },
36522
36523     afterSlideIn : function(){
36524         this.clearAutoHide();
36525         this.isSlid = false;
36526         this.clearMonitor();
36527         this.el.setStyle("z-index", "");
36528         if(this.collapseBtn){
36529             this.collapseBtn.show();
36530         }
36531         this.closeBtn.setStyle('display', this.closeBtnState);
36532         if(this.stickBtn){
36533             this.stickBtn.hide();
36534         }
36535         this.fireEvent("slidehide", this);
36536     },
36537
36538     slideIn : function(cb){
36539         if(!this.isSlid || this.el.hasActiveFx()){
36540             Roo.callback(cb);
36541             return;
36542         }
36543         this.isSlid = false;
36544         this.beforeSlide();
36545         this.el.slideOut(this.getSlideAnchor(), {
36546             callback: function(){
36547                 this.el.setLeftTop(-10000, -10000);
36548                 this.afterSlide();
36549                 this.afterSlideIn();
36550                 Roo.callback(cb);
36551             },
36552             scope: this,
36553             block: true
36554         });
36555     },
36556     
36557     slideInIf : function(e){
36558         if(!e.within(this.el)){
36559             this.slideIn();
36560         }
36561     },
36562
36563     animateCollapse : function(){
36564         this.beforeSlide();
36565         this.el.setStyle("z-index", 20000);
36566         var anchor = this.getSlideAnchor();
36567         this.el.slideOut(anchor, {
36568             callback : function(){
36569                 this.el.setStyle("z-index", "");
36570                 this.collapsedEl.slideIn(anchor, {duration:.3});
36571                 this.afterSlide();
36572                 this.el.setLocation(-10000,-10000);
36573                 this.el.hide();
36574                 this.fireEvent("collapsed", this);
36575             },
36576             scope: this,
36577             block: true
36578         });
36579     },
36580
36581     animateExpand : function(){
36582         this.beforeSlide();
36583         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36584         this.el.setStyle("z-index", 20000);
36585         this.collapsedEl.hide({
36586             duration:.1
36587         });
36588         this.el.slideIn(this.getSlideAnchor(), {
36589             callback : function(){
36590                 this.el.setStyle("z-index", "");
36591                 this.afterSlide();
36592                 if(this.split){
36593                     this.split.el.show();
36594                 }
36595                 this.fireEvent("invalidated", this);
36596                 this.fireEvent("expanded", this);
36597             },
36598             scope: this,
36599             block: true
36600         });
36601     },
36602
36603     anchors : {
36604         "west" : "left",
36605         "east" : "right",
36606         "north" : "top",
36607         "south" : "bottom"
36608     },
36609
36610     sanchors : {
36611         "west" : "l",
36612         "east" : "r",
36613         "north" : "t",
36614         "south" : "b"
36615     },
36616
36617     canchors : {
36618         "west" : "tl-tr",
36619         "east" : "tr-tl",
36620         "north" : "tl-bl",
36621         "south" : "bl-tl"
36622     },
36623
36624     getAnchor : function(){
36625         return this.anchors[this.position];
36626     },
36627
36628     getCollapseAnchor : function(){
36629         return this.canchors[this.position];
36630     },
36631
36632     getSlideAnchor : function(){
36633         return this.sanchors[this.position];
36634     },
36635
36636     getAlignAdj : function(){
36637         var cm = this.cmargins;
36638         switch(this.position){
36639             case "west":
36640                 return [0, 0];
36641             break;
36642             case "east":
36643                 return [0, 0];
36644             break;
36645             case "north":
36646                 return [0, 0];
36647             break;
36648             case "south":
36649                 return [0, 0];
36650             break;
36651         }
36652     },
36653
36654     getExpandAdj : function(){
36655         var c = this.collapsedEl, cm = this.cmargins;
36656         switch(this.position){
36657             case "west":
36658                 return [-(cm.right+c.getWidth()+cm.left), 0];
36659             break;
36660             case "east":
36661                 return [cm.right+c.getWidth()+cm.left, 0];
36662             break;
36663             case "north":
36664                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36665             break;
36666             case "south":
36667                 return [0, cm.top+cm.bottom+c.getHeight()];
36668             break;
36669         }
36670     }
36671 });/*
36672  * Based on:
36673  * Ext JS Library 1.1.1
36674  * Copyright(c) 2006-2007, Ext JS, LLC.
36675  *
36676  * Originally Released Under LGPL - original licence link has changed is not relivant.
36677  *
36678  * Fork - LGPL
36679  * <script type="text/javascript">
36680  */
36681 /*
36682  * These classes are private internal classes
36683  */
36684 Roo.bootstrap.layout.Center = function(config){
36685     config.region = "center";
36686     Roo.bootstrap.layout.Region.call(this, config);
36687     this.visible = true;
36688     this.minWidth = config.minWidth || 20;
36689     this.minHeight = config.minHeight || 20;
36690 };
36691
36692 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36693     hide : function(){
36694         // center panel can't be hidden
36695     },
36696     
36697     show : function(){
36698         // center panel can't be hidden
36699     },
36700     
36701     getMinWidth: function(){
36702         return this.minWidth;
36703     },
36704     
36705     getMinHeight: function(){
36706         return this.minHeight;
36707     }
36708 });
36709
36710
36711
36712
36713  
36714
36715
36716
36717
36718
36719 Roo.bootstrap.layout.North = function(config)
36720 {
36721     config.region = 'north';
36722     config.cursor = 'n-resize';
36723     
36724     Roo.bootstrap.layout.Split.call(this, config);
36725     
36726     
36727     if(this.split){
36728         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36729         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36730         this.split.el.addClass("roo-layout-split-v");
36731     }
36732     var size = config.initialSize || config.height;
36733     if(typeof size != "undefined"){
36734         this.el.setHeight(size);
36735     }
36736 };
36737 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36738 {
36739     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36740     
36741     
36742     
36743     getBox : function(){
36744         if(this.collapsed){
36745             return this.collapsedEl.getBox();
36746         }
36747         var box = this.el.getBox();
36748         if(this.split){
36749             box.height += this.split.el.getHeight();
36750         }
36751         return box;
36752     },
36753     
36754     updateBox : function(box){
36755         if(this.split && !this.collapsed){
36756             box.height -= this.split.el.getHeight();
36757             this.split.el.setLeft(box.x);
36758             this.split.el.setTop(box.y+box.height);
36759             this.split.el.setWidth(box.width);
36760         }
36761         if(this.collapsed){
36762             this.updateBody(box.width, null);
36763         }
36764         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36765     }
36766 });
36767
36768
36769
36770
36771
36772 Roo.bootstrap.layout.South = function(config){
36773     config.region = 'south';
36774     config.cursor = 's-resize';
36775     Roo.bootstrap.layout.Split.call(this, config);
36776     if(this.split){
36777         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36778         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36779         this.split.el.addClass("roo-layout-split-v");
36780     }
36781     var size = config.initialSize || config.height;
36782     if(typeof size != "undefined"){
36783         this.el.setHeight(size);
36784     }
36785 };
36786
36787 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36788     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36789     getBox : function(){
36790         if(this.collapsed){
36791             return this.collapsedEl.getBox();
36792         }
36793         var box = this.el.getBox();
36794         if(this.split){
36795             var sh = this.split.el.getHeight();
36796             box.height += sh;
36797             box.y -= sh;
36798         }
36799         return box;
36800     },
36801     
36802     updateBox : function(box){
36803         if(this.split && !this.collapsed){
36804             var sh = this.split.el.getHeight();
36805             box.height -= sh;
36806             box.y += sh;
36807             this.split.el.setLeft(box.x);
36808             this.split.el.setTop(box.y-sh);
36809             this.split.el.setWidth(box.width);
36810         }
36811         if(this.collapsed){
36812             this.updateBody(box.width, null);
36813         }
36814         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36815     }
36816 });
36817
36818 Roo.bootstrap.layout.East = function(config){
36819     config.region = "east";
36820     config.cursor = "e-resize";
36821     Roo.bootstrap.layout.Split.call(this, config);
36822     if(this.split){
36823         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36824         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36825         this.split.el.addClass("roo-layout-split-h");
36826     }
36827     var size = config.initialSize || config.width;
36828     if(typeof size != "undefined"){
36829         this.el.setWidth(size);
36830     }
36831 };
36832 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36833     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36834     getBox : function(){
36835         if(this.collapsed){
36836             return this.collapsedEl.getBox();
36837         }
36838         var box = this.el.getBox();
36839         if(this.split){
36840             var sw = this.split.el.getWidth();
36841             box.width += sw;
36842             box.x -= sw;
36843         }
36844         return box;
36845     },
36846
36847     updateBox : function(box){
36848         if(this.split && !this.collapsed){
36849             var sw = this.split.el.getWidth();
36850             box.width -= sw;
36851             this.split.el.setLeft(box.x);
36852             this.split.el.setTop(box.y);
36853             this.split.el.setHeight(box.height);
36854             box.x += sw;
36855         }
36856         if(this.collapsed){
36857             this.updateBody(null, box.height);
36858         }
36859         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36860     }
36861 });
36862
36863 Roo.bootstrap.layout.West = function(config){
36864     config.region = "west";
36865     config.cursor = "w-resize";
36866     
36867     Roo.bootstrap.layout.Split.call(this, config);
36868     if(this.split){
36869         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36870         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36871         this.split.el.addClass("roo-layout-split-h");
36872     }
36873     
36874 };
36875 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36876     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36877     
36878     onRender: function(ctr, pos)
36879     {
36880         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36881         var size = this.config.initialSize || this.config.width;
36882         if(typeof size != "undefined"){
36883             this.el.setWidth(size);
36884         }
36885     },
36886     
36887     getBox : function(){
36888         if(this.collapsed){
36889             return this.collapsedEl.getBox();
36890         }
36891         var box = this.el.getBox();
36892         if(this.split){
36893             box.width += this.split.el.getWidth();
36894         }
36895         return box;
36896     },
36897     
36898     updateBox : function(box){
36899         if(this.split && !this.collapsed){
36900             var sw = this.split.el.getWidth();
36901             box.width -= sw;
36902             this.split.el.setLeft(box.x+box.width);
36903             this.split.el.setTop(box.y);
36904             this.split.el.setHeight(box.height);
36905         }
36906         if(this.collapsed){
36907             this.updateBody(null, box.height);
36908         }
36909         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36910     }
36911 });
36912 Roo.namespace("Roo.bootstrap.panel");/*
36913  * Based on:
36914  * Ext JS Library 1.1.1
36915  * Copyright(c) 2006-2007, Ext JS, LLC.
36916  *
36917  * Originally Released Under LGPL - original licence link has changed is not relivant.
36918  *
36919  * Fork - LGPL
36920  * <script type="text/javascript">
36921  */
36922 /**
36923  * @class Roo.ContentPanel
36924  * @extends Roo.util.Observable
36925  * A basic ContentPanel element.
36926  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36927  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36928  * @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
36929  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36930  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36931  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36932  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36933  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36934  * @cfg {String} title          The title for this panel
36935  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36936  * @cfg {String} url            Calls {@link #setUrl} with this value
36937  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36938  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36939  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36940  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36941  * @cfg {Boolean} badges render the badges
36942
36943  * @constructor
36944  * Create a new ContentPanel.
36945  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36946  * @param {String/Object} config A string to set only the title or a config object
36947  * @param {String} content (optional) Set the HTML content for this panel
36948  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36949  */
36950 Roo.bootstrap.panel.Content = function( config){
36951     
36952     this.tpl = config.tpl || false;
36953     
36954     var el = config.el;
36955     var content = config.content;
36956
36957     if(config.autoCreate){ // xtype is available if this is called from factory
36958         el = Roo.id();
36959     }
36960     this.el = Roo.get(el);
36961     if(!this.el && config && config.autoCreate){
36962         if(typeof config.autoCreate == "object"){
36963             if(!config.autoCreate.id){
36964                 config.autoCreate.id = config.id||el;
36965             }
36966             this.el = Roo.DomHelper.append(document.body,
36967                         config.autoCreate, true);
36968         }else{
36969             var elcfg =  {   tag: "div",
36970                             cls: "roo-layout-inactive-content",
36971                             id: config.id||el
36972                             };
36973             if (config.html) {
36974                 elcfg.html = config.html;
36975                 
36976             }
36977                         
36978             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36979         }
36980     } 
36981     this.closable = false;
36982     this.loaded = false;
36983     this.active = false;
36984    
36985       
36986     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36987         
36988         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36989         
36990         this.wrapEl = this.el; //this.el.wrap();
36991         var ti = [];
36992         if (config.toolbar.items) {
36993             ti = config.toolbar.items ;
36994             delete config.toolbar.items ;
36995         }
36996         
36997         var nitems = [];
36998         this.toolbar.render(this.wrapEl, 'before');
36999         for(var i =0;i < ti.length;i++) {
37000           //  Roo.log(['add child', items[i]]);
37001             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37002         }
37003         this.toolbar.items = nitems;
37004         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37005         delete config.toolbar;
37006         
37007     }
37008     /*
37009     // xtype created footer. - not sure if will work as we normally have to render first..
37010     if (this.footer && !this.footer.el && this.footer.xtype) {
37011         if (!this.wrapEl) {
37012             this.wrapEl = this.el.wrap();
37013         }
37014     
37015         this.footer.container = this.wrapEl.createChild();
37016          
37017         this.footer = Roo.factory(this.footer, Roo);
37018         
37019     }
37020     */
37021     
37022      if(typeof config == "string"){
37023         this.title = config;
37024     }else{
37025         Roo.apply(this, config);
37026     }
37027     
37028     if(this.resizeEl){
37029         this.resizeEl = Roo.get(this.resizeEl, true);
37030     }else{
37031         this.resizeEl = this.el;
37032     }
37033     // handle view.xtype
37034     
37035  
37036     
37037     
37038     this.addEvents({
37039         /**
37040          * @event activate
37041          * Fires when this panel is activated. 
37042          * @param {Roo.ContentPanel} this
37043          */
37044         "activate" : true,
37045         /**
37046          * @event deactivate
37047          * Fires when this panel is activated. 
37048          * @param {Roo.ContentPanel} this
37049          */
37050         "deactivate" : true,
37051
37052         /**
37053          * @event resize
37054          * Fires when this panel is resized if fitToFrame is true.
37055          * @param {Roo.ContentPanel} this
37056          * @param {Number} width The width after any component adjustments
37057          * @param {Number} height The height after any component adjustments
37058          */
37059         "resize" : true,
37060         
37061          /**
37062          * @event render
37063          * Fires when this tab is created
37064          * @param {Roo.ContentPanel} this
37065          */
37066         "render" : true
37067         
37068         
37069         
37070     });
37071     
37072
37073     
37074     
37075     if(this.autoScroll){
37076         this.resizeEl.setStyle("overflow", "auto");
37077     } else {
37078         // fix randome scrolling
37079         //this.el.on('scroll', function() {
37080         //    Roo.log('fix random scolling');
37081         //    this.scrollTo('top',0); 
37082         //});
37083     }
37084     content = content || this.content;
37085     if(content){
37086         this.setContent(content);
37087     }
37088     if(config && config.url){
37089         this.setUrl(this.url, this.params, this.loadOnce);
37090     }
37091     
37092     
37093     
37094     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37095     
37096     if (this.view && typeof(this.view.xtype) != 'undefined') {
37097         this.view.el = this.el.appendChild(document.createElement("div"));
37098         this.view = Roo.factory(this.view); 
37099         this.view.render  &&  this.view.render(false, '');  
37100     }
37101     
37102     
37103     this.fireEvent('render', this);
37104 };
37105
37106 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37107     
37108     tabTip : '',
37109     
37110     setRegion : function(region){
37111         this.region = region;
37112         this.setActiveClass(region && !this.background);
37113     },
37114     
37115     
37116     setActiveClass: function(state)
37117     {
37118         if(state){
37119            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37120            this.el.setStyle('position','relative');
37121         }else{
37122            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37123            this.el.setStyle('position', 'absolute');
37124         } 
37125     },
37126     
37127     /**
37128      * Returns the toolbar for this Panel if one was configured. 
37129      * @return {Roo.Toolbar} 
37130      */
37131     getToolbar : function(){
37132         return this.toolbar;
37133     },
37134     
37135     setActiveState : function(active)
37136     {
37137         this.active = active;
37138         this.setActiveClass(active);
37139         if(!active){
37140             if(this.fireEvent("deactivate", this) === false){
37141                 return false;
37142             }
37143             return true;
37144         }
37145         this.fireEvent("activate", this);
37146         return true;
37147     },
37148     /**
37149      * Updates this panel's element
37150      * @param {String} content The new content
37151      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37152     */
37153     setContent : function(content, loadScripts){
37154         this.el.update(content, loadScripts);
37155     },
37156
37157     ignoreResize : function(w, h){
37158         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37159             return true;
37160         }else{
37161             this.lastSize = {width: w, height: h};
37162             return false;
37163         }
37164     },
37165     /**
37166      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37167      * @return {Roo.UpdateManager} The UpdateManager
37168      */
37169     getUpdateManager : function(){
37170         return this.el.getUpdateManager();
37171     },
37172      /**
37173      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37174      * @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:
37175 <pre><code>
37176 panel.load({
37177     url: "your-url.php",
37178     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37179     callback: yourFunction,
37180     scope: yourObject, //(optional scope)
37181     discardUrl: false,
37182     nocache: false,
37183     text: "Loading...",
37184     timeout: 30,
37185     scripts: false
37186 });
37187 </code></pre>
37188      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37189      * 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.
37190      * @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}
37191      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37192      * @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.
37193      * @return {Roo.ContentPanel} this
37194      */
37195     load : function(){
37196         var um = this.el.getUpdateManager();
37197         um.update.apply(um, arguments);
37198         return this;
37199     },
37200
37201
37202     /**
37203      * 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.
37204      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37205      * @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)
37206      * @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)
37207      * @return {Roo.UpdateManager} The UpdateManager
37208      */
37209     setUrl : function(url, params, loadOnce){
37210         if(this.refreshDelegate){
37211             this.removeListener("activate", this.refreshDelegate);
37212         }
37213         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37214         this.on("activate", this.refreshDelegate);
37215         return this.el.getUpdateManager();
37216     },
37217     
37218     _handleRefresh : function(url, params, loadOnce){
37219         if(!loadOnce || !this.loaded){
37220             var updater = this.el.getUpdateManager();
37221             updater.update(url, params, this._setLoaded.createDelegate(this));
37222         }
37223     },
37224     
37225     _setLoaded : function(){
37226         this.loaded = true;
37227     }, 
37228     
37229     /**
37230      * Returns this panel's id
37231      * @return {String} 
37232      */
37233     getId : function(){
37234         return this.el.id;
37235     },
37236     
37237     /** 
37238      * Returns this panel's element - used by regiosn to add.
37239      * @return {Roo.Element} 
37240      */
37241     getEl : function(){
37242         return this.wrapEl || this.el;
37243     },
37244     
37245    
37246     
37247     adjustForComponents : function(width, height)
37248     {
37249         //Roo.log('adjustForComponents ');
37250         if(this.resizeEl != this.el){
37251             width -= this.el.getFrameWidth('lr');
37252             height -= this.el.getFrameWidth('tb');
37253         }
37254         if(this.toolbar){
37255             var te = this.toolbar.getEl();
37256             te.setWidth(width);
37257             height -= te.getHeight();
37258         }
37259         if(this.footer){
37260             var te = this.footer.getEl();
37261             te.setWidth(width);
37262             height -= te.getHeight();
37263         }
37264         
37265         
37266         if(this.adjustments){
37267             width += this.adjustments[0];
37268             height += this.adjustments[1];
37269         }
37270         return {"width": width, "height": height};
37271     },
37272     
37273     setSize : function(width, height){
37274         if(this.fitToFrame && !this.ignoreResize(width, height)){
37275             if(this.fitContainer && this.resizeEl != this.el){
37276                 this.el.setSize(width, height);
37277             }
37278             var size = this.adjustForComponents(width, height);
37279             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37280             this.fireEvent('resize', this, size.width, size.height);
37281         }
37282     },
37283     
37284     /**
37285      * Returns this panel's title
37286      * @return {String} 
37287      */
37288     getTitle : function(){
37289         
37290         if (typeof(this.title) != 'object') {
37291             return this.title;
37292         }
37293         
37294         var t = '';
37295         for (var k in this.title) {
37296             if (!this.title.hasOwnProperty(k)) {
37297                 continue;
37298             }
37299             
37300             if (k.indexOf('-') >= 0) {
37301                 var s = k.split('-');
37302                 for (var i = 0; i<s.length; i++) {
37303                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37304                 }
37305             } else {
37306                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37307             }
37308         }
37309         return t;
37310     },
37311     
37312     /**
37313      * Set this panel's title
37314      * @param {String} title
37315      */
37316     setTitle : function(title){
37317         this.title = title;
37318         if(this.region){
37319             this.region.updatePanelTitle(this, title);
37320         }
37321     },
37322     
37323     /**
37324      * Returns true is this panel was configured to be closable
37325      * @return {Boolean} 
37326      */
37327     isClosable : function(){
37328         return this.closable;
37329     },
37330     
37331     beforeSlide : function(){
37332         this.el.clip();
37333         this.resizeEl.clip();
37334     },
37335     
37336     afterSlide : function(){
37337         this.el.unclip();
37338         this.resizeEl.unclip();
37339     },
37340     
37341     /**
37342      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37343      *   Will fail silently if the {@link #setUrl} method has not been called.
37344      *   This does not activate the panel, just updates its content.
37345      */
37346     refresh : function(){
37347         if(this.refreshDelegate){
37348            this.loaded = false;
37349            this.refreshDelegate();
37350         }
37351     },
37352     
37353     /**
37354      * Destroys this panel
37355      */
37356     destroy : function(){
37357         this.el.removeAllListeners();
37358         var tempEl = document.createElement("span");
37359         tempEl.appendChild(this.el.dom);
37360         tempEl.innerHTML = "";
37361         this.el.remove();
37362         this.el = null;
37363     },
37364     
37365     /**
37366      * form - if the content panel contains a form - this is a reference to it.
37367      * @type {Roo.form.Form}
37368      */
37369     form : false,
37370     /**
37371      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37372      *    This contains a reference to it.
37373      * @type {Roo.View}
37374      */
37375     view : false,
37376     
37377       /**
37378      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37379      * <pre><code>
37380
37381 layout.addxtype({
37382        xtype : 'Form',
37383        items: [ .... ]
37384    }
37385 );
37386
37387 </code></pre>
37388      * @param {Object} cfg Xtype definition of item to add.
37389      */
37390     
37391     
37392     getChildContainer: function () {
37393         return this.getEl();
37394     }
37395     
37396     
37397     /*
37398         var  ret = new Roo.factory(cfg);
37399         return ret;
37400         
37401         
37402         // add form..
37403         if (cfg.xtype.match(/^Form$/)) {
37404             
37405             var el;
37406             //if (this.footer) {
37407             //    el = this.footer.container.insertSibling(false, 'before');
37408             //} else {
37409                 el = this.el.createChild();
37410             //}
37411
37412             this.form = new  Roo.form.Form(cfg);
37413             
37414             
37415             if ( this.form.allItems.length) {
37416                 this.form.render(el.dom);
37417             }
37418             return this.form;
37419         }
37420         // should only have one of theses..
37421         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37422             // views.. should not be just added - used named prop 'view''
37423             
37424             cfg.el = this.el.appendChild(document.createElement("div"));
37425             // factory?
37426             
37427             var ret = new Roo.factory(cfg);
37428              
37429              ret.render && ret.render(false, ''); // render blank..
37430             this.view = ret;
37431             return ret;
37432         }
37433         return false;
37434     }
37435     \*/
37436 });
37437  
37438 /**
37439  * @class Roo.bootstrap.panel.Grid
37440  * @extends Roo.bootstrap.panel.Content
37441  * @constructor
37442  * Create a new GridPanel.
37443  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37444  * @param {Object} config A the config object
37445   
37446  */
37447
37448
37449
37450 Roo.bootstrap.panel.Grid = function(config)
37451 {
37452     
37453       
37454     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37455         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37456
37457     config.el = this.wrapper;
37458     //this.el = this.wrapper;
37459     
37460       if (config.container) {
37461         // ctor'ed from a Border/panel.grid
37462         
37463         
37464         this.wrapper.setStyle("overflow", "hidden");
37465         this.wrapper.addClass('roo-grid-container');
37466
37467     }
37468     
37469     
37470     if(config.toolbar){
37471         var tool_el = this.wrapper.createChild();    
37472         this.toolbar = Roo.factory(config.toolbar);
37473         var ti = [];
37474         if (config.toolbar.items) {
37475             ti = config.toolbar.items ;
37476             delete config.toolbar.items ;
37477         }
37478         
37479         var nitems = [];
37480         this.toolbar.render(tool_el);
37481         for(var i =0;i < ti.length;i++) {
37482           //  Roo.log(['add child', items[i]]);
37483             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37484         }
37485         this.toolbar.items = nitems;
37486         
37487         delete config.toolbar;
37488     }
37489     
37490     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37491     config.grid.scrollBody = true;;
37492     config.grid.monitorWindowResize = false; // turn off autosizing
37493     config.grid.autoHeight = false;
37494     config.grid.autoWidth = false;
37495     
37496     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37497     
37498     if (config.background) {
37499         // render grid on panel activation (if panel background)
37500         this.on('activate', function(gp) {
37501             if (!gp.grid.rendered) {
37502                 gp.grid.render(this.wrapper);
37503                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37504             }
37505         });
37506             
37507     } else {
37508         this.grid.render(this.wrapper);
37509         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37510
37511     }
37512     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37513     // ??? needed ??? config.el = this.wrapper;
37514     
37515     
37516     
37517   
37518     // xtype created footer. - not sure if will work as we normally have to render first..
37519     if (this.footer && !this.footer.el && this.footer.xtype) {
37520         
37521         var ctr = this.grid.getView().getFooterPanel(true);
37522         this.footer.dataSource = this.grid.dataSource;
37523         this.footer = Roo.factory(this.footer, Roo);
37524         this.footer.render(ctr);
37525         
37526     }
37527     
37528     
37529     
37530     
37531      
37532 };
37533
37534 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37535     getId : function(){
37536         return this.grid.id;
37537     },
37538     
37539     /**
37540      * Returns the grid for this panel
37541      * @return {Roo.bootstrap.Table} 
37542      */
37543     getGrid : function(){
37544         return this.grid;    
37545     },
37546     
37547     setSize : function(width, height){
37548         if(!this.ignoreResize(width, height)){
37549             var grid = this.grid;
37550             var size = this.adjustForComponents(width, height);
37551             var gridel = grid.getGridEl();
37552             gridel.setSize(size.width, size.height);
37553             /*
37554             var thd = grid.getGridEl().select('thead',true).first();
37555             var tbd = grid.getGridEl().select('tbody', true).first();
37556             if (tbd) {
37557                 tbd.setSize(width, height - thd.getHeight());
37558             }
37559             */
37560             grid.autoSize();
37561         }
37562     },
37563      
37564     
37565     
37566     beforeSlide : function(){
37567         this.grid.getView().scroller.clip();
37568     },
37569     
37570     afterSlide : function(){
37571         this.grid.getView().scroller.unclip();
37572     },
37573     
37574     destroy : function(){
37575         this.grid.destroy();
37576         delete this.grid;
37577         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37578     }
37579 });
37580
37581 /**
37582  * @class Roo.bootstrap.panel.Nest
37583  * @extends Roo.bootstrap.panel.Content
37584  * @constructor
37585  * Create a new Panel, that can contain a layout.Border.
37586  * 
37587  * 
37588  * @param {Roo.BorderLayout} layout The layout for this panel
37589  * @param {String/Object} config A string to set only the title or a config object
37590  */
37591 Roo.bootstrap.panel.Nest = function(config)
37592 {
37593     // construct with only one argument..
37594     /* FIXME - implement nicer consturctors
37595     if (layout.layout) {
37596         config = layout;
37597         layout = config.layout;
37598         delete config.layout;
37599     }
37600     if (layout.xtype && !layout.getEl) {
37601         // then layout needs constructing..
37602         layout = Roo.factory(layout, Roo);
37603     }
37604     */
37605     
37606     config.el =  config.layout.getEl();
37607     
37608     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37609     
37610     config.layout.monitorWindowResize = false; // turn off autosizing
37611     this.layout = config.layout;
37612     this.layout.getEl().addClass("roo-layout-nested-layout");
37613     
37614     
37615     
37616     
37617 };
37618
37619 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37620
37621     setSize : function(width, height){
37622         if(!this.ignoreResize(width, height)){
37623             var size = this.adjustForComponents(width, height);
37624             var el = this.layout.getEl();
37625             if (size.height < 1) {
37626                 el.setWidth(size.width);   
37627             } else {
37628                 el.setSize(size.width, size.height);
37629             }
37630             var touch = el.dom.offsetWidth;
37631             this.layout.layout();
37632             // ie requires a double layout on the first pass
37633             if(Roo.isIE && !this.initialized){
37634                 this.initialized = true;
37635                 this.layout.layout();
37636             }
37637         }
37638     },
37639     
37640     // activate all subpanels if not currently active..
37641     
37642     setActiveState : function(active){
37643         this.active = active;
37644         this.setActiveClass(active);
37645         
37646         if(!active){
37647             this.fireEvent("deactivate", this);
37648             return;
37649         }
37650         
37651         this.fireEvent("activate", this);
37652         // not sure if this should happen before or after..
37653         if (!this.layout) {
37654             return; // should not happen..
37655         }
37656         var reg = false;
37657         for (var r in this.layout.regions) {
37658             reg = this.layout.getRegion(r);
37659             if (reg.getActivePanel()) {
37660                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37661                 reg.setActivePanel(reg.getActivePanel());
37662                 continue;
37663             }
37664             if (!reg.panels.length) {
37665                 continue;
37666             }
37667             reg.showPanel(reg.getPanel(0));
37668         }
37669         
37670         
37671         
37672         
37673     },
37674     
37675     /**
37676      * Returns the nested BorderLayout for this panel
37677      * @return {Roo.BorderLayout} 
37678      */
37679     getLayout : function(){
37680         return this.layout;
37681     },
37682     
37683      /**
37684      * Adds a xtype elements to the layout of the nested panel
37685      * <pre><code>
37686
37687 panel.addxtype({
37688        xtype : 'ContentPanel',
37689        region: 'west',
37690        items: [ .... ]
37691    }
37692 );
37693
37694 panel.addxtype({
37695         xtype : 'NestedLayoutPanel',
37696         region: 'west',
37697         layout: {
37698            center: { },
37699            west: { }   
37700         },
37701         items : [ ... list of content panels or nested layout panels.. ]
37702    }
37703 );
37704 </code></pre>
37705      * @param {Object} cfg Xtype definition of item to add.
37706      */
37707     addxtype : function(cfg) {
37708         return this.layout.addxtype(cfg);
37709     
37710     }
37711 });        /*
37712  * Based on:
37713  * Ext JS Library 1.1.1
37714  * Copyright(c) 2006-2007, Ext JS, LLC.
37715  *
37716  * Originally Released Under LGPL - original licence link has changed is not relivant.
37717  *
37718  * Fork - LGPL
37719  * <script type="text/javascript">
37720  */
37721 /**
37722  * @class Roo.TabPanel
37723  * @extends Roo.util.Observable
37724  * A lightweight tab container.
37725  * <br><br>
37726  * Usage:
37727  * <pre><code>
37728 // basic tabs 1, built from existing content
37729 var tabs = new Roo.TabPanel("tabs1");
37730 tabs.addTab("script", "View Script");
37731 tabs.addTab("markup", "View Markup");
37732 tabs.activate("script");
37733
37734 // more advanced tabs, built from javascript
37735 var jtabs = new Roo.TabPanel("jtabs");
37736 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37737
37738 // set up the UpdateManager
37739 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37740 var updater = tab2.getUpdateManager();
37741 updater.setDefaultUrl("ajax1.htm");
37742 tab2.on('activate', updater.refresh, updater, true);
37743
37744 // Use setUrl for Ajax loading
37745 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37746 tab3.setUrl("ajax2.htm", null, true);
37747
37748 // Disabled tab
37749 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37750 tab4.disable();
37751
37752 jtabs.activate("jtabs-1");
37753  * </code></pre>
37754  * @constructor
37755  * Create a new TabPanel.
37756  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37757  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37758  */
37759 Roo.bootstrap.panel.Tabs = function(config){
37760     /**
37761     * The container element for this TabPanel.
37762     * @type Roo.Element
37763     */
37764     this.el = Roo.get(config.el);
37765     delete config.el;
37766     if(config){
37767         if(typeof config == "boolean"){
37768             this.tabPosition = config ? "bottom" : "top";
37769         }else{
37770             Roo.apply(this, config);
37771         }
37772     }
37773     
37774     if(this.tabPosition == "bottom"){
37775         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37776         this.el.addClass("roo-tabs-bottom");
37777     }
37778     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37779     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37780     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37781     if(Roo.isIE){
37782         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37783     }
37784     if(this.tabPosition != "bottom"){
37785         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37786          * @type Roo.Element
37787          */
37788         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37789         this.el.addClass("roo-tabs-top");
37790     }
37791     this.items = [];
37792
37793     this.bodyEl.setStyle("position", "relative");
37794
37795     this.active = null;
37796     this.activateDelegate = this.activate.createDelegate(this);
37797
37798     this.addEvents({
37799         /**
37800          * @event tabchange
37801          * Fires when the active tab changes
37802          * @param {Roo.TabPanel} this
37803          * @param {Roo.TabPanelItem} activePanel The new active tab
37804          */
37805         "tabchange": true,
37806         /**
37807          * @event beforetabchange
37808          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37809          * @param {Roo.TabPanel} this
37810          * @param {Object} e Set cancel to true on this object to cancel the tab change
37811          * @param {Roo.TabPanelItem} tab The tab being changed to
37812          */
37813         "beforetabchange" : true
37814     });
37815
37816     Roo.EventManager.onWindowResize(this.onResize, this);
37817     this.cpad = this.el.getPadding("lr");
37818     this.hiddenCount = 0;
37819
37820
37821     // toolbar on the tabbar support...
37822     if (this.toolbar) {
37823         alert("no toolbar support yet");
37824         this.toolbar  = false;
37825         /*
37826         var tcfg = this.toolbar;
37827         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37828         this.toolbar = new Roo.Toolbar(tcfg);
37829         if (Roo.isSafari) {
37830             var tbl = tcfg.container.child('table', true);
37831             tbl.setAttribute('width', '100%');
37832         }
37833         */
37834         
37835     }
37836    
37837
37838
37839     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37840 };
37841
37842 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37843     /*
37844      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37845      */
37846     tabPosition : "top",
37847     /*
37848      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37849      */
37850     currentTabWidth : 0,
37851     /*
37852      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37853      */
37854     minTabWidth : 40,
37855     /*
37856      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37857      */
37858     maxTabWidth : 250,
37859     /*
37860      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37861      */
37862     preferredTabWidth : 175,
37863     /*
37864      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37865      */
37866     resizeTabs : false,
37867     /*
37868      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37869      */
37870     monitorResize : true,
37871     /*
37872      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37873      */
37874     toolbar : false,
37875
37876     /**
37877      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37878      * @param {String} id The id of the div to use <b>or create</b>
37879      * @param {String} text The text for the tab
37880      * @param {String} content (optional) Content to put in the TabPanelItem body
37881      * @param {Boolean} closable (optional) True to create a close icon on the tab
37882      * @return {Roo.TabPanelItem} The created TabPanelItem
37883      */
37884     addTab : function(id, text, content, closable, tpl)
37885     {
37886         var item = new Roo.bootstrap.panel.TabItem({
37887             panel: this,
37888             id : id,
37889             text : text,
37890             closable : closable,
37891             tpl : tpl
37892         });
37893         this.addTabItem(item);
37894         if(content){
37895             item.setContent(content);
37896         }
37897         return item;
37898     },
37899
37900     /**
37901      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37902      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37903      * @return {Roo.TabPanelItem}
37904      */
37905     getTab : function(id){
37906         return this.items[id];
37907     },
37908
37909     /**
37910      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37911      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37912      */
37913     hideTab : function(id){
37914         var t = this.items[id];
37915         if(!t.isHidden()){
37916            t.setHidden(true);
37917            this.hiddenCount++;
37918            this.autoSizeTabs();
37919         }
37920     },
37921
37922     /**
37923      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37924      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37925      */
37926     unhideTab : function(id){
37927         var t = this.items[id];
37928         if(t.isHidden()){
37929            t.setHidden(false);
37930            this.hiddenCount--;
37931            this.autoSizeTabs();
37932         }
37933     },
37934
37935     /**
37936      * Adds an existing {@link Roo.TabPanelItem}.
37937      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37938      */
37939     addTabItem : function(item){
37940         this.items[item.id] = item;
37941         this.items.push(item);
37942       //  if(this.resizeTabs){
37943     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37944   //         this.autoSizeTabs();
37945 //        }else{
37946 //            item.autoSize();
37947        // }
37948     },
37949
37950     /**
37951      * Removes a {@link Roo.TabPanelItem}.
37952      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37953      */
37954     removeTab : function(id){
37955         var items = this.items;
37956         var tab = items[id];
37957         if(!tab) { return; }
37958         var index = items.indexOf(tab);
37959         if(this.active == tab && items.length > 1){
37960             var newTab = this.getNextAvailable(index);
37961             if(newTab) {
37962                 newTab.activate();
37963             }
37964         }
37965         this.stripEl.dom.removeChild(tab.pnode.dom);
37966         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37967             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37968         }
37969         items.splice(index, 1);
37970         delete this.items[tab.id];
37971         tab.fireEvent("close", tab);
37972         tab.purgeListeners();
37973         this.autoSizeTabs();
37974     },
37975
37976     getNextAvailable : function(start){
37977         var items = this.items;
37978         var index = start;
37979         // look for a next tab that will slide over to
37980         // replace the one being removed
37981         while(index < items.length){
37982             var item = items[++index];
37983             if(item && !item.isHidden()){
37984                 return item;
37985             }
37986         }
37987         // if one isn't found select the previous tab (on the left)
37988         index = start;
37989         while(index >= 0){
37990             var item = items[--index];
37991             if(item && !item.isHidden()){
37992                 return item;
37993             }
37994         }
37995         return null;
37996     },
37997
37998     /**
37999      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38000      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38001      */
38002     disableTab : function(id){
38003         var tab = this.items[id];
38004         if(tab && this.active != tab){
38005             tab.disable();
38006         }
38007     },
38008
38009     /**
38010      * Enables a {@link Roo.TabPanelItem} that is disabled.
38011      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38012      */
38013     enableTab : function(id){
38014         var tab = this.items[id];
38015         tab.enable();
38016     },
38017
38018     /**
38019      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38020      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38021      * @return {Roo.TabPanelItem} The TabPanelItem.
38022      */
38023     activate : function(id){
38024         var tab = this.items[id];
38025         if(!tab){
38026             return null;
38027         }
38028         if(tab == this.active || tab.disabled){
38029             return tab;
38030         }
38031         var e = {};
38032         this.fireEvent("beforetabchange", this, e, tab);
38033         if(e.cancel !== true && !tab.disabled){
38034             if(this.active){
38035                 this.active.hide();
38036             }
38037             this.active = this.items[id];
38038             this.active.show();
38039             this.fireEvent("tabchange", this, this.active);
38040         }
38041         return tab;
38042     },
38043
38044     /**
38045      * Gets the active {@link Roo.TabPanelItem}.
38046      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38047      */
38048     getActiveTab : function(){
38049         return this.active;
38050     },
38051
38052     /**
38053      * Updates the tab body element to fit the height of the container element
38054      * for overflow scrolling
38055      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38056      */
38057     syncHeight : function(targetHeight){
38058         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38059         var bm = this.bodyEl.getMargins();
38060         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38061         this.bodyEl.setHeight(newHeight);
38062         return newHeight;
38063     },
38064
38065     onResize : function(){
38066         if(this.monitorResize){
38067             this.autoSizeTabs();
38068         }
38069     },
38070
38071     /**
38072      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38073      */
38074     beginUpdate : function(){
38075         this.updating = true;
38076     },
38077
38078     /**
38079      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38080      */
38081     endUpdate : function(){
38082         this.updating = false;
38083         this.autoSizeTabs();
38084     },
38085
38086     /**
38087      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38088      */
38089     autoSizeTabs : function(){
38090         var count = this.items.length;
38091         var vcount = count - this.hiddenCount;
38092         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38093             return;
38094         }
38095         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38096         var availWidth = Math.floor(w / vcount);
38097         var b = this.stripBody;
38098         if(b.getWidth() > w){
38099             var tabs = this.items;
38100             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38101             if(availWidth < this.minTabWidth){
38102                 /*if(!this.sleft){    // incomplete scrolling code
38103                     this.createScrollButtons();
38104                 }
38105                 this.showScroll();
38106                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38107             }
38108         }else{
38109             if(this.currentTabWidth < this.preferredTabWidth){
38110                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38111             }
38112         }
38113     },
38114
38115     /**
38116      * Returns the number of tabs in this TabPanel.
38117      * @return {Number}
38118      */
38119      getCount : function(){
38120          return this.items.length;
38121      },
38122
38123     /**
38124      * Resizes all the tabs to the passed width
38125      * @param {Number} The new width
38126      */
38127     setTabWidth : function(width){
38128         this.currentTabWidth = width;
38129         for(var i = 0, len = this.items.length; i < len; i++) {
38130                 if(!this.items[i].isHidden()) {
38131                 this.items[i].setWidth(width);
38132             }
38133         }
38134     },
38135
38136     /**
38137      * Destroys this TabPanel
38138      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38139      */
38140     destroy : function(removeEl){
38141         Roo.EventManager.removeResizeListener(this.onResize, this);
38142         for(var i = 0, len = this.items.length; i < len; i++){
38143             this.items[i].purgeListeners();
38144         }
38145         if(removeEl === true){
38146             this.el.update("");
38147             this.el.remove();
38148         }
38149     },
38150     
38151     createStrip : function(container)
38152     {
38153         var strip = document.createElement("nav");
38154         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38155         container.appendChild(strip);
38156         return strip;
38157     },
38158     
38159     createStripList : function(strip)
38160     {
38161         // div wrapper for retard IE
38162         // returns the "tr" element.
38163         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38164         //'<div class="x-tabs-strip-wrap">'+
38165           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38166           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38167         return strip.firstChild; //.firstChild.firstChild.firstChild;
38168     },
38169     createBody : function(container)
38170     {
38171         var body = document.createElement("div");
38172         Roo.id(body, "tab-body");
38173         //Roo.fly(body).addClass("x-tabs-body");
38174         Roo.fly(body).addClass("tab-content");
38175         container.appendChild(body);
38176         return body;
38177     },
38178     createItemBody :function(bodyEl, id){
38179         var body = Roo.getDom(id);
38180         if(!body){
38181             body = document.createElement("div");
38182             body.id = id;
38183         }
38184         //Roo.fly(body).addClass("x-tabs-item-body");
38185         Roo.fly(body).addClass("tab-pane");
38186          bodyEl.insertBefore(body, bodyEl.firstChild);
38187         return body;
38188     },
38189     /** @private */
38190     createStripElements :  function(stripEl, text, closable, tpl)
38191     {
38192         var td = document.createElement("li"); // was td..
38193         
38194         
38195         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38196         
38197         
38198         stripEl.appendChild(td);
38199         /*if(closable){
38200             td.className = "x-tabs-closable";
38201             if(!this.closeTpl){
38202                 this.closeTpl = new Roo.Template(
38203                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38204                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38205                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38206                 );
38207             }
38208             var el = this.closeTpl.overwrite(td, {"text": text});
38209             var close = el.getElementsByTagName("div")[0];
38210             var inner = el.getElementsByTagName("em")[0];
38211             return {"el": el, "close": close, "inner": inner};
38212         } else {
38213         */
38214         // not sure what this is..
38215 //            if(!this.tabTpl){
38216                 //this.tabTpl = new Roo.Template(
38217                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38218                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38219                 //);
38220 //                this.tabTpl = new Roo.Template(
38221 //                   '<a href="#">' +
38222 //                   '<span unselectable="on"' +
38223 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38224 //                            ' >{text}</span></a>'
38225 //                );
38226 //                
38227 //            }
38228
38229
38230             var template = tpl || this.tabTpl || false;
38231             
38232             if(!template){
38233                 
38234                 template = new Roo.Template(
38235                    '<a href="#">' +
38236                    '<span unselectable="on"' +
38237                             (this.disableTooltips ? '' : ' title="{text}"') +
38238                             ' >{text}</span></a>'
38239                 );
38240             }
38241             
38242             switch (typeof(template)) {
38243                 case 'object' :
38244                     break;
38245                 case 'string' :
38246                     template = new Roo.Template(template);
38247                     break;
38248                 default :
38249                     break;
38250             }
38251             
38252             var el = template.overwrite(td, {"text": text});
38253             
38254             var inner = el.getElementsByTagName("span")[0];
38255             
38256             return {"el": el, "inner": inner};
38257             
38258     }
38259         
38260     
38261 });
38262
38263 /**
38264  * @class Roo.TabPanelItem
38265  * @extends Roo.util.Observable
38266  * Represents an individual item (tab plus body) in a TabPanel.
38267  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38268  * @param {String} id The id of this TabPanelItem
38269  * @param {String} text The text for the tab of this TabPanelItem
38270  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38271  */
38272 Roo.bootstrap.panel.TabItem = function(config){
38273     /**
38274      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38275      * @type Roo.TabPanel
38276      */
38277     this.tabPanel = config.panel;
38278     /**
38279      * The id for this TabPanelItem
38280      * @type String
38281      */
38282     this.id = config.id;
38283     /** @private */
38284     this.disabled = false;
38285     /** @private */
38286     this.text = config.text;
38287     /** @private */
38288     this.loaded = false;
38289     this.closable = config.closable;
38290
38291     /**
38292      * The body element for this TabPanelItem.
38293      * @type Roo.Element
38294      */
38295     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38296     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38297     this.bodyEl.setStyle("display", "block");
38298     this.bodyEl.setStyle("zoom", "1");
38299     //this.hideAction();
38300
38301     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38302     /** @private */
38303     this.el = Roo.get(els.el);
38304     this.inner = Roo.get(els.inner, true);
38305     this.textEl = Roo.get(this.el.dom.firstChild, true);
38306     this.pnode = Roo.get(els.el.parentNode, true);
38307 //    this.el.on("mousedown", this.onTabMouseDown, this);
38308     this.el.on("click", this.onTabClick, this);
38309     /** @private */
38310     if(config.closable){
38311         var c = Roo.get(els.close, true);
38312         c.dom.title = this.closeText;
38313         c.addClassOnOver("close-over");
38314         c.on("click", this.closeClick, this);
38315      }
38316
38317     this.addEvents({
38318          /**
38319          * @event activate
38320          * Fires when this tab becomes the active tab.
38321          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38322          * @param {Roo.TabPanelItem} this
38323          */
38324         "activate": true,
38325         /**
38326          * @event beforeclose
38327          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38328          * @param {Roo.TabPanelItem} this
38329          * @param {Object} e Set cancel to true on this object to cancel the close.
38330          */
38331         "beforeclose": true,
38332         /**
38333          * @event close
38334          * Fires when this tab is closed.
38335          * @param {Roo.TabPanelItem} this
38336          */
38337          "close": true,
38338         /**
38339          * @event deactivate
38340          * Fires when this tab is no longer the active tab.
38341          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38342          * @param {Roo.TabPanelItem} this
38343          */
38344          "deactivate" : true
38345     });
38346     this.hidden = false;
38347
38348     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38349 };
38350
38351 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38352            {
38353     purgeListeners : function(){
38354        Roo.util.Observable.prototype.purgeListeners.call(this);
38355        this.el.removeAllListeners();
38356     },
38357     /**
38358      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38359      */
38360     show : function(){
38361         this.pnode.addClass("active");
38362         this.showAction();
38363         if(Roo.isOpera){
38364             this.tabPanel.stripWrap.repaint();
38365         }
38366         this.fireEvent("activate", this.tabPanel, this);
38367     },
38368
38369     /**
38370      * Returns true if this tab is the active tab.
38371      * @return {Boolean}
38372      */
38373     isActive : function(){
38374         return this.tabPanel.getActiveTab() == this;
38375     },
38376
38377     /**
38378      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38379      */
38380     hide : function(){
38381         this.pnode.removeClass("active");
38382         this.hideAction();
38383         this.fireEvent("deactivate", this.tabPanel, this);
38384     },
38385
38386     hideAction : function(){
38387         this.bodyEl.hide();
38388         this.bodyEl.setStyle("position", "absolute");
38389         this.bodyEl.setLeft("-20000px");
38390         this.bodyEl.setTop("-20000px");
38391     },
38392
38393     showAction : function(){
38394         this.bodyEl.setStyle("position", "relative");
38395         this.bodyEl.setTop("");
38396         this.bodyEl.setLeft("");
38397         this.bodyEl.show();
38398     },
38399
38400     /**
38401      * Set the tooltip for the tab.
38402      * @param {String} tooltip The tab's tooltip
38403      */
38404     setTooltip : function(text){
38405         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38406             this.textEl.dom.qtip = text;
38407             this.textEl.dom.removeAttribute('title');
38408         }else{
38409             this.textEl.dom.title = text;
38410         }
38411     },
38412
38413     onTabClick : function(e){
38414         e.preventDefault();
38415         this.tabPanel.activate(this.id);
38416     },
38417
38418     onTabMouseDown : function(e){
38419         e.preventDefault();
38420         this.tabPanel.activate(this.id);
38421     },
38422 /*
38423     getWidth : function(){
38424         return this.inner.getWidth();
38425     },
38426
38427     setWidth : function(width){
38428         var iwidth = width - this.pnode.getPadding("lr");
38429         this.inner.setWidth(iwidth);
38430         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38431         this.pnode.setWidth(width);
38432     },
38433 */
38434     /**
38435      * Show or hide the tab
38436      * @param {Boolean} hidden True to hide or false to show.
38437      */
38438     setHidden : function(hidden){
38439         this.hidden = hidden;
38440         this.pnode.setStyle("display", hidden ? "none" : "");
38441     },
38442
38443     /**
38444      * Returns true if this tab is "hidden"
38445      * @return {Boolean}
38446      */
38447     isHidden : function(){
38448         return this.hidden;
38449     },
38450
38451     /**
38452      * Returns the text for this tab
38453      * @return {String}
38454      */
38455     getText : function(){
38456         return this.text;
38457     },
38458     /*
38459     autoSize : function(){
38460         //this.el.beginMeasure();
38461         this.textEl.setWidth(1);
38462         /*
38463          *  #2804 [new] Tabs in Roojs
38464          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38465          */
38466         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38467         //this.el.endMeasure();
38468     //},
38469
38470     /**
38471      * Sets the text for the tab (Note: this also sets the tooltip text)
38472      * @param {String} text The tab's text and tooltip
38473      */
38474     setText : function(text){
38475         this.text = text;
38476         this.textEl.update(text);
38477         this.setTooltip(text);
38478         //if(!this.tabPanel.resizeTabs){
38479         //    this.autoSize();
38480         //}
38481     },
38482     /**
38483      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38484      */
38485     activate : function(){
38486         this.tabPanel.activate(this.id);
38487     },
38488
38489     /**
38490      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38491      */
38492     disable : function(){
38493         if(this.tabPanel.active != this){
38494             this.disabled = true;
38495             this.pnode.addClass("disabled");
38496         }
38497     },
38498
38499     /**
38500      * Enables this TabPanelItem if it was previously disabled.
38501      */
38502     enable : function(){
38503         this.disabled = false;
38504         this.pnode.removeClass("disabled");
38505     },
38506
38507     /**
38508      * Sets the content for this TabPanelItem.
38509      * @param {String} content The content
38510      * @param {Boolean} loadScripts true to look for and load scripts
38511      */
38512     setContent : function(content, loadScripts){
38513         this.bodyEl.update(content, loadScripts);
38514     },
38515
38516     /**
38517      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38518      * @return {Roo.UpdateManager} The UpdateManager
38519      */
38520     getUpdateManager : function(){
38521         return this.bodyEl.getUpdateManager();
38522     },
38523
38524     /**
38525      * Set a URL to be used to load the content for this TabPanelItem.
38526      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38527      * @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)
38528      * @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)
38529      * @return {Roo.UpdateManager} The UpdateManager
38530      */
38531     setUrl : function(url, params, loadOnce){
38532         if(this.refreshDelegate){
38533             this.un('activate', this.refreshDelegate);
38534         }
38535         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38536         this.on("activate", this.refreshDelegate);
38537         return this.bodyEl.getUpdateManager();
38538     },
38539
38540     /** @private */
38541     _handleRefresh : function(url, params, loadOnce){
38542         if(!loadOnce || !this.loaded){
38543             var updater = this.bodyEl.getUpdateManager();
38544             updater.update(url, params, this._setLoaded.createDelegate(this));
38545         }
38546     },
38547
38548     /**
38549      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38550      *   Will fail silently if the setUrl method has not been called.
38551      *   This does not activate the panel, just updates its content.
38552      */
38553     refresh : function(){
38554         if(this.refreshDelegate){
38555            this.loaded = false;
38556            this.refreshDelegate();
38557         }
38558     },
38559
38560     /** @private */
38561     _setLoaded : function(){
38562         this.loaded = true;
38563     },
38564
38565     /** @private */
38566     closeClick : function(e){
38567         var o = {};
38568         e.stopEvent();
38569         this.fireEvent("beforeclose", this, o);
38570         if(o.cancel !== true){
38571             this.tabPanel.removeTab(this.id);
38572         }
38573     },
38574     /**
38575      * The text displayed in the tooltip for the close icon.
38576      * @type String
38577      */
38578     closeText : "Close this tab"
38579 });
38580 /**
38581 *    This script refer to:
38582 *    Title: International Telephone Input
38583 *    Author: Jack O'Connor
38584 *    Code version:  v12.1.12
38585 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38586 **/
38587
38588 Roo.bootstrap.PhoneInputData = function() {
38589     var d = [
38590       [
38591         "Afghanistan (‫افغانستان‬‎)",
38592         "af",
38593         "93"
38594       ],
38595       [
38596         "Albania (Shqipëri)",
38597         "al",
38598         "355"
38599       ],
38600       [
38601         "Algeria (‫الجزائر‬‎)",
38602         "dz",
38603         "213"
38604       ],
38605       [
38606         "American Samoa",
38607         "as",
38608         "1684"
38609       ],
38610       [
38611         "Andorra",
38612         "ad",
38613         "376"
38614       ],
38615       [
38616         "Angola",
38617         "ao",
38618         "244"
38619       ],
38620       [
38621         "Anguilla",
38622         "ai",
38623         "1264"
38624       ],
38625       [
38626         "Antigua and Barbuda",
38627         "ag",
38628         "1268"
38629       ],
38630       [
38631         "Argentina",
38632         "ar",
38633         "54"
38634       ],
38635       [
38636         "Armenia (Հայաստան)",
38637         "am",
38638         "374"
38639       ],
38640       [
38641         "Aruba",
38642         "aw",
38643         "297"
38644       ],
38645       [
38646         "Australia",
38647         "au",
38648         "61",
38649         0
38650       ],
38651       [
38652         "Austria (Österreich)",
38653         "at",
38654         "43"
38655       ],
38656       [
38657         "Azerbaijan (Azərbaycan)",
38658         "az",
38659         "994"
38660       ],
38661       [
38662         "Bahamas",
38663         "bs",
38664         "1242"
38665       ],
38666       [
38667         "Bahrain (‫البحرين‬‎)",
38668         "bh",
38669         "973"
38670       ],
38671       [
38672         "Bangladesh (বাংলাদেশ)",
38673         "bd",
38674         "880"
38675       ],
38676       [
38677         "Barbados",
38678         "bb",
38679         "1246"
38680       ],
38681       [
38682         "Belarus (Беларусь)",
38683         "by",
38684         "375"
38685       ],
38686       [
38687         "Belgium (België)",
38688         "be",
38689         "32"
38690       ],
38691       [
38692         "Belize",
38693         "bz",
38694         "501"
38695       ],
38696       [
38697         "Benin (Bénin)",
38698         "bj",
38699         "229"
38700       ],
38701       [
38702         "Bermuda",
38703         "bm",
38704         "1441"
38705       ],
38706       [
38707         "Bhutan (འབྲུག)",
38708         "bt",
38709         "975"
38710       ],
38711       [
38712         "Bolivia",
38713         "bo",
38714         "591"
38715       ],
38716       [
38717         "Bosnia and Herzegovina (Босна и Херцеговина)",
38718         "ba",
38719         "387"
38720       ],
38721       [
38722         "Botswana",
38723         "bw",
38724         "267"
38725       ],
38726       [
38727         "Brazil (Brasil)",
38728         "br",
38729         "55"
38730       ],
38731       [
38732         "British Indian Ocean Territory",
38733         "io",
38734         "246"
38735       ],
38736       [
38737         "British Virgin Islands",
38738         "vg",
38739         "1284"
38740       ],
38741       [
38742         "Brunei",
38743         "bn",
38744         "673"
38745       ],
38746       [
38747         "Bulgaria (България)",
38748         "bg",
38749         "359"
38750       ],
38751       [
38752         "Burkina Faso",
38753         "bf",
38754         "226"
38755       ],
38756       [
38757         "Burundi (Uburundi)",
38758         "bi",
38759         "257"
38760       ],
38761       [
38762         "Cambodia (កម្ពុជា)",
38763         "kh",
38764         "855"
38765       ],
38766       [
38767         "Cameroon (Cameroun)",
38768         "cm",
38769         "237"
38770       ],
38771       [
38772         "Canada",
38773         "ca",
38774         "1",
38775         1,
38776         ["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"]
38777       ],
38778       [
38779         "Cape Verde (Kabu Verdi)",
38780         "cv",
38781         "238"
38782       ],
38783       [
38784         "Caribbean Netherlands",
38785         "bq",
38786         "599",
38787         1
38788       ],
38789       [
38790         "Cayman Islands",
38791         "ky",
38792         "1345"
38793       ],
38794       [
38795         "Central African Republic (République centrafricaine)",
38796         "cf",
38797         "236"
38798       ],
38799       [
38800         "Chad (Tchad)",
38801         "td",
38802         "235"
38803       ],
38804       [
38805         "Chile",
38806         "cl",
38807         "56"
38808       ],
38809       [
38810         "China (中国)",
38811         "cn",
38812         "86"
38813       ],
38814       [
38815         "Christmas Island",
38816         "cx",
38817         "61",
38818         2
38819       ],
38820       [
38821         "Cocos (Keeling) Islands",
38822         "cc",
38823         "61",
38824         1
38825       ],
38826       [
38827         "Colombia",
38828         "co",
38829         "57"
38830       ],
38831       [
38832         "Comoros (‫جزر القمر‬‎)",
38833         "km",
38834         "269"
38835       ],
38836       [
38837         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38838         "cd",
38839         "243"
38840       ],
38841       [
38842         "Congo (Republic) (Congo-Brazzaville)",
38843         "cg",
38844         "242"
38845       ],
38846       [
38847         "Cook Islands",
38848         "ck",
38849         "682"
38850       ],
38851       [
38852         "Costa Rica",
38853         "cr",
38854         "506"
38855       ],
38856       [
38857         "Côte d’Ivoire",
38858         "ci",
38859         "225"
38860       ],
38861       [
38862         "Croatia (Hrvatska)",
38863         "hr",
38864         "385"
38865       ],
38866       [
38867         "Cuba",
38868         "cu",
38869         "53"
38870       ],
38871       [
38872         "Curaçao",
38873         "cw",
38874         "599",
38875         0
38876       ],
38877       [
38878         "Cyprus (Κύπρος)",
38879         "cy",
38880         "357"
38881       ],
38882       [
38883         "Czech Republic (Česká republika)",
38884         "cz",
38885         "420"
38886       ],
38887       [
38888         "Denmark (Danmark)",
38889         "dk",
38890         "45"
38891       ],
38892       [
38893         "Djibouti",
38894         "dj",
38895         "253"
38896       ],
38897       [
38898         "Dominica",
38899         "dm",
38900         "1767"
38901       ],
38902       [
38903         "Dominican Republic (República Dominicana)",
38904         "do",
38905         "1",
38906         2,
38907         ["809", "829", "849"]
38908       ],
38909       [
38910         "Ecuador",
38911         "ec",
38912         "593"
38913       ],
38914       [
38915         "Egypt (‫مصر‬‎)",
38916         "eg",
38917         "20"
38918       ],
38919       [
38920         "El Salvador",
38921         "sv",
38922         "503"
38923       ],
38924       [
38925         "Equatorial Guinea (Guinea Ecuatorial)",
38926         "gq",
38927         "240"
38928       ],
38929       [
38930         "Eritrea",
38931         "er",
38932         "291"
38933       ],
38934       [
38935         "Estonia (Eesti)",
38936         "ee",
38937         "372"
38938       ],
38939       [
38940         "Ethiopia",
38941         "et",
38942         "251"
38943       ],
38944       [
38945         "Falkland Islands (Islas Malvinas)",
38946         "fk",
38947         "500"
38948       ],
38949       [
38950         "Faroe Islands (Føroyar)",
38951         "fo",
38952         "298"
38953       ],
38954       [
38955         "Fiji",
38956         "fj",
38957         "679"
38958       ],
38959       [
38960         "Finland (Suomi)",
38961         "fi",
38962         "358",
38963         0
38964       ],
38965       [
38966         "France",
38967         "fr",
38968         "33"
38969       ],
38970       [
38971         "French Guiana (Guyane française)",
38972         "gf",
38973         "594"
38974       ],
38975       [
38976         "French Polynesia (Polynésie française)",
38977         "pf",
38978         "689"
38979       ],
38980       [
38981         "Gabon",
38982         "ga",
38983         "241"
38984       ],
38985       [
38986         "Gambia",
38987         "gm",
38988         "220"
38989       ],
38990       [
38991         "Georgia (საქართველო)",
38992         "ge",
38993         "995"
38994       ],
38995       [
38996         "Germany (Deutschland)",
38997         "de",
38998         "49"
38999       ],
39000       [
39001         "Ghana (Gaana)",
39002         "gh",
39003         "233"
39004       ],
39005       [
39006         "Gibraltar",
39007         "gi",
39008         "350"
39009       ],
39010       [
39011         "Greece (Ελλάδα)",
39012         "gr",
39013         "30"
39014       ],
39015       [
39016         "Greenland (Kalaallit Nunaat)",
39017         "gl",
39018         "299"
39019       ],
39020       [
39021         "Grenada",
39022         "gd",
39023         "1473"
39024       ],
39025       [
39026         "Guadeloupe",
39027         "gp",
39028         "590",
39029         0
39030       ],
39031       [
39032         "Guam",
39033         "gu",
39034         "1671"
39035       ],
39036       [
39037         "Guatemala",
39038         "gt",
39039         "502"
39040       ],
39041       [
39042         "Guernsey",
39043         "gg",
39044         "44",
39045         1
39046       ],
39047       [
39048         "Guinea (Guinée)",
39049         "gn",
39050         "224"
39051       ],
39052       [
39053         "Guinea-Bissau (Guiné Bissau)",
39054         "gw",
39055         "245"
39056       ],
39057       [
39058         "Guyana",
39059         "gy",
39060         "592"
39061       ],
39062       [
39063         "Haiti",
39064         "ht",
39065         "509"
39066       ],
39067       [
39068         "Honduras",
39069         "hn",
39070         "504"
39071       ],
39072       [
39073         "Hong Kong (香港)",
39074         "hk",
39075         "852"
39076       ],
39077       [
39078         "Hungary (Magyarország)",
39079         "hu",
39080         "36"
39081       ],
39082       [
39083         "Iceland (Ísland)",
39084         "is",
39085         "354"
39086       ],
39087       [
39088         "India (भारत)",
39089         "in",
39090         "91"
39091       ],
39092       [
39093         "Indonesia",
39094         "id",
39095         "62"
39096       ],
39097       [
39098         "Iran (‫ایران‬‎)",
39099         "ir",
39100         "98"
39101       ],
39102       [
39103         "Iraq (‫العراق‬‎)",
39104         "iq",
39105         "964"
39106       ],
39107       [
39108         "Ireland",
39109         "ie",
39110         "353"
39111       ],
39112       [
39113         "Isle of Man",
39114         "im",
39115         "44",
39116         2
39117       ],
39118       [
39119         "Israel (‫ישראל‬‎)",
39120         "il",
39121         "972"
39122       ],
39123       [
39124         "Italy (Italia)",
39125         "it",
39126         "39",
39127         0
39128       ],
39129       [
39130         "Jamaica",
39131         "jm",
39132         "1876"
39133       ],
39134       [
39135         "Japan (日本)",
39136         "jp",
39137         "81"
39138       ],
39139       [
39140         "Jersey",
39141         "je",
39142         "44",
39143         3
39144       ],
39145       [
39146         "Jordan (‫الأردن‬‎)",
39147         "jo",
39148         "962"
39149       ],
39150       [
39151         "Kazakhstan (Казахстан)",
39152         "kz",
39153         "7",
39154         1
39155       ],
39156       [
39157         "Kenya",
39158         "ke",
39159         "254"
39160       ],
39161       [
39162         "Kiribati",
39163         "ki",
39164         "686"
39165       ],
39166       [
39167         "Kosovo",
39168         "xk",
39169         "383"
39170       ],
39171       [
39172         "Kuwait (‫الكويت‬‎)",
39173         "kw",
39174         "965"
39175       ],
39176       [
39177         "Kyrgyzstan (Кыргызстан)",
39178         "kg",
39179         "996"
39180       ],
39181       [
39182         "Laos (ລາວ)",
39183         "la",
39184         "856"
39185       ],
39186       [
39187         "Latvia (Latvija)",
39188         "lv",
39189         "371"
39190       ],
39191       [
39192         "Lebanon (‫لبنان‬‎)",
39193         "lb",
39194         "961"
39195       ],
39196       [
39197         "Lesotho",
39198         "ls",
39199         "266"
39200       ],
39201       [
39202         "Liberia",
39203         "lr",
39204         "231"
39205       ],
39206       [
39207         "Libya (‫ليبيا‬‎)",
39208         "ly",
39209         "218"
39210       ],
39211       [
39212         "Liechtenstein",
39213         "li",
39214         "423"
39215       ],
39216       [
39217         "Lithuania (Lietuva)",
39218         "lt",
39219         "370"
39220       ],
39221       [
39222         "Luxembourg",
39223         "lu",
39224         "352"
39225       ],
39226       [
39227         "Macau (澳門)",
39228         "mo",
39229         "853"
39230       ],
39231       [
39232         "Macedonia (FYROM) (Македонија)",
39233         "mk",
39234         "389"
39235       ],
39236       [
39237         "Madagascar (Madagasikara)",
39238         "mg",
39239         "261"
39240       ],
39241       [
39242         "Malawi",
39243         "mw",
39244         "265"
39245       ],
39246       [
39247         "Malaysia",
39248         "my",
39249         "60"
39250       ],
39251       [
39252         "Maldives",
39253         "mv",
39254         "960"
39255       ],
39256       [
39257         "Mali",
39258         "ml",
39259         "223"
39260       ],
39261       [
39262         "Malta",
39263         "mt",
39264         "356"
39265       ],
39266       [
39267         "Marshall Islands",
39268         "mh",
39269         "692"
39270       ],
39271       [
39272         "Martinique",
39273         "mq",
39274         "596"
39275       ],
39276       [
39277         "Mauritania (‫موريتانيا‬‎)",
39278         "mr",
39279         "222"
39280       ],
39281       [
39282         "Mauritius (Moris)",
39283         "mu",
39284         "230"
39285       ],
39286       [
39287         "Mayotte",
39288         "yt",
39289         "262",
39290         1
39291       ],
39292       [
39293         "Mexico (México)",
39294         "mx",
39295         "52"
39296       ],
39297       [
39298         "Micronesia",
39299         "fm",
39300         "691"
39301       ],
39302       [
39303         "Moldova (Republica Moldova)",
39304         "md",
39305         "373"
39306       ],
39307       [
39308         "Monaco",
39309         "mc",
39310         "377"
39311       ],
39312       [
39313         "Mongolia (Монгол)",
39314         "mn",
39315         "976"
39316       ],
39317       [
39318         "Montenegro (Crna Gora)",
39319         "me",
39320         "382"
39321       ],
39322       [
39323         "Montserrat",
39324         "ms",
39325         "1664"
39326       ],
39327       [
39328         "Morocco (‫المغرب‬‎)",
39329         "ma",
39330         "212",
39331         0
39332       ],
39333       [
39334         "Mozambique (Moçambique)",
39335         "mz",
39336         "258"
39337       ],
39338       [
39339         "Myanmar (Burma) (မြန်မာ)",
39340         "mm",
39341         "95"
39342       ],
39343       [
39344         "Namibia (Namibië)",
39345         "na",
39346         "264"
39347       ],
39348       [
39349         "Nauru",
39350         "nr",
39351         "674"
39352       ],
39353       [
39354         "Nepal (नेपाल)",
39355         "np",
39356         "977"
39357       ],
39358       [
39359         "Netherlands (Nederland)",
39360         "nl",
39361         "31"
39362       ],
39363       [
39364         "New Caledonia (Nouvelle-Calédonie)",
39365         "nc",
39366         "687"
39367       ],
39368       [
39369         "New Zealand",
39370         "nz",
39371         "64"
39372       ],
39373       [
39374         "Nicaragua",
39375         "ni",
39376         "505"
39377       ],
39378       [
39379         "Niger (Nijar)",
39380         "ne",
39381         "227"
39382       ],
39383       [
39384         "Nigeria",
39385         "ng",
39386         "234"
39387       ],
39388       [
39389         "Niue",
39390         "nu",
39391         "683"
39392       ],
39393       [
39394         "Norfolk Island",
39395         "nf",
39396         "672"
39397       ],
39398       [
39399         "North Korea (조선 민주주의 인민 공화국)",
39400         "kp",
39401         "850"
39402       ],
39403       [
39404         "Northern Mariana Islands",
39405         "mp",
39406         "1670"
39407       ],
39408       [
39409         "Norway (Norge)",
39410         "no",
39411         "47",
39412         0
39413       ],
39414       [
39415         "Oman (‫عُمان‬‎)",
39416         "om",
39417         "968"
39418       ],
39419       [
39420         "Pakistan (‫پاکستان‬‎)",
39421         "pk",
39422         "92"
39423       ],
39424       [
39425         "Palau",
39426         "pw",
39427         "680"
39428       ],
39429       [
39430         "Palestine (‫فلسطين‬‎)",
39431         "ps",
39432         "970"
39433       ],
39434       [
39435         "Panama (Panamá)",
39436         "pa",
39437         "507"
39438       ],
39439       [
39440         "Papua New Guinea",
39441         "pg",
39442         "675"
39443       ],
39444       [
39445         "Paraguay",
39446         "py",
39447         "595"
39448       ],
39449       [
39450         "Peru (Perú)",
39451         "pe",
39452         "51"
39453       ],
39454       [
39455         "Philippines",
39456         "ph",
39457         "63"
39458       ],
39459       [
39460         "Poland (Polska)",
39461         "pl",
39462         "48"
39463       ],
39464       [
39465         "Portugal",
39466         "pt",
39467         "351"
39468       ],
39469       [
39470         "Puerto Rico",
39471         "pr",
39472         "1",
39473         3,
39474         ["787", "939"]
39475       ],
39476       [
39477         "Qatar (‫قطر‬‎)",
39478         "qa",
39479         "974"
39480       ],
39481       [
39482         "Réunion (La Réunion)",
39483         "re",
39484         "262",
39485         0
39486       ],
39487       [
39488         "Romania (România)",
39489         "ro",
39490         "40"
39491       ],
39492       [
39493         "Russia (Россия)",
39494         "ru",
39495         "7",
39496         0
39497       ],
39498       [
39499         "Rwanda",
39500         "rw",
39501         "250"
39502       ],
39503       [
39504         "Saint Barthélemy",
39505         "bl",
39506         "590",
39507         1
39508       ],
39509       [
39510         "Saint Helena",
39511         "sh",
39512         "290"
39513       ],
39514       [
39515         "Saint Kitts and Nevis",
39516         "kn",
39517         "1869"
39518       ],
39519       [
39520         "Saint Lucia",
39521         "lc",
39522         "1758"
39523       ],
39524       [
39525         "Saint Martin (Saint-Martin (partie française))",
39526         "mf",
39527         "590",
39528         2
39529       ],
39530       [
39531         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39532         "pm",
39533         "508"
39534       ],
39535       [
39536         "Saint Vincent and the Grenadines",
39537         "vc",
39538         "1784"
39539       ],
39540       [
39541         "Samoa",
39542         "ws",
39543         "685"
39544       ],
39545       [
39546         "San Marino",
39547         "sm",
39548         "378"
39549       ],
39550       [
39551         "São Tomé and Príncipe (São Tomé e Príncipe)",
39552         "st",
39553         "239"
39554       ],
39555       [
39556         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39557         "sa",
39558         "966"
39559       ],
39560       [
39561         "Senegal (Sénégal)",
39562         "sn",
39563         "221"
39564       ],
39565       [
39566         "Serbia (Србија)",
39567         "rs",
39568         "381"
39569       ],
39570       [
39571         "Seychelles",
39572         "sc",
39573         "248"
39574       ],
39575       [
39576         "Sierra Leone",
39577         "sl",
39578         "232"
39579       ],
39580       [
39581         "Singapore",
39582         "sg",
39583         "65"
39584       ],
39585       [
39586         "Sint Maarten",
39587         "sx",
39588         "1721"
39589       ],
39590       [
39591         "Slovakia (Slovensko)",
39592         "sk",
39593         "421"
39594       ],
39595       [
39596         "Slovenia (Slovenija)",
39597         "si",
39598         "386"
39599       ],
39600       [
39601         "Solomon Islands",
39602         "sb",
39603         "677"
39604       ],
39605       [
39606         "Somalia (Soomaaliya)",
39607         "so",
39608         "252"
39609       ],
39610       [
39611         "South Africa",
39612         "za",
39613         "27"
39614       ],
39615       [
39616         "South Korea (대한민국)",
39617         "kr",
39618         "82"
39619       ],
39620       [
39621         "South Sudan (‫جنوب السودان‬‎)",
39622         "ss",
39623         "211"
39624       ],
39625       [
39626         "Spain (España)",
39627         "es",
39628         "34"
39629       ],
39630       [
39631         "Sri Lanka (ශ්‍රී ලංකාව)",
39632         "lk",
39633         "94"
39634       ],
39635       [
39636         "Sudan (‫السودان‬‎)",
39637         "sd",
39638         "249"
39639       ],
39640       [
39641         "Suriname",
39642         "sr",
39643         "597"
39644       ],
39645       [
39646         "Svalbard and Jan Mayen",
39647         "sj",
39648         "47",
39649         1
39650       ],
39651       [
39652         "Swaziland",
39653         "sz",
39654         "268"
39655       ],
39656       [
39657         "Sweden (Sverige)",
39658         "se",
39659         "46"
39660       ],
39661       [
39662         "Switzerland (Schweiz)",
39663         "ch",
39664         "41"
39665       ],
39666       [
39667         "Syria (‫سوريا‬‎)",
39668         "sy",
39669         "963"
39670       ],
39671       [
39672         "Taiwan (台灣)",
39673         "tw",
39674         "886"
39675       ],
39676       [
39677         "Tajikistan",
39678         "tj",
39679         "992"
39680       ],
39681       [
39682         "Tanzania",
39683         "tz",
39684         "255"
39685       ],
39686       [
39687         "Thailand (ไทย)",
39688         "th",
39689         "66"
39690       ],
39691       [
39692         "Timor-Leste",
39693         "tl",
39694         "670"
39695       ],
39696       [
39697         "Togo",
39698         "tg",
39699         "228"
39700       ],
39701       [
39702         "Tokelau",
39703         "tk",
39704         "690"
39705       ],
39706       [
39707         "Tonga",
39708         "to",
39709         "676"
39710       ],
39711       [
39712         "Trinidad and Tobago",
39713         "tt",
39714         "1868"
39715       ],
39716       [
39717         "Tunisia (‫تونس‬‎)",
39718         "tn",
39719         "216"
39720       ],
39721       [
39722         "Turkey (Türkiye)",
39723         "tr",
39724         "90"
39725       ],
39726       [
39727         "Turkmenistan",
39728         "tm",
39729         "993"
39730       ],
39731       [
39732         "Turks and Caicos Islands",
39733         "tc",
39734         "1649"
39735       ],
39736       [
39737         "Tuvalu",
39738         "tv",
39739         "688"
39740       ],
39741       [
39742         "U.S. Virgin Islands",
39743         "vi",
39744         "1340"
39745       ],
39746       [
39747         "Uganda",
39748         "ug",
39749         "256"
39750       ],
39751       [
39752         "Ukraine (Україна)",
39753         "ua",
39754         "380"
39755       ],
39756       [
39757         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39758         "ae",
39759         "971"
39760       ],
39761       [
39762         "United Kingdom",
39763         "gb",
39764         "44",
39765         0
39766       ],
39767       [
39768         "United States",
39769         "us",
39770         "1",
39771         0
39772       ],
39773       [
39774         "Uruguay",
39775         "uy",
39776         "598"
39777       ],
39778       [
39779         "Uzbekistan (Oʻzbekiston)",
39780         "uz",
39781         "998"
39782       ],
39783       [
39784         "Vanuatu",
39785         "vu",
39786         "678"
39787       ],
39788       [
39789         "Vatican City (Città del Vaticano)",
39790         "va",
39791         "39",
39792         1
39793       ],
39794       [
39795         "Venezuela",
39796         "ve",
39797         "58"
39798       ],
39799       [
39800         "Vietnam (Việt Nam)",
39801         "vn",
39802         "84"
39803       ],
39804       [
39805         "Wallis and Futuna (Wallis-et-Futuna)",
39806         "wf",
39807         "681"
39808       ],
39809       [
39810         "Western Sahara (‫الصحراء الغربية‬‎)",
39811         "eh",
39812         "212",
39813         1
39814       ],
39815       [
39816         "Yemen (‫اليمن‬‎)",
39817         "ye",
39818         "967"
39819       ],
39820       [
39821         "Zambia",
39822         "zm",
39823         "260"
39824       ],
39825       [
39826         "Zimbabwe",
39827         "zw",
39828         "263"
39829       ],
39830       [
39831         "Åland Islands",
39832         "ax",
39833         "358",
39834         1
39835       ]
39836   ];
39837   
39838   return d;
39839 }/**
39840 *    This script refer to:
39841 *    Title: International Telephone Input
39842 *    Author: Jack O'Connor
39843 *    Code version:  v12.1.12
39844 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39845 **/
39846
39847 /**
39848  * @class Roo.bootstrap.PhoneInput
39849  * @extends Roo.bootstrap.TriggerField
39850  * An input with International dial-code selection
39851  
39852  * @cfg {String} defaultDialCode default '+852'
39853  * @cfg {Array} preferedCountries default []
39854   
39855  * @constructor
39856  * Create a new PhoneInput.
39857  * @param {Object} config Configuration options
39858  */
39859
39860 Roo.bootstrap.PhoneInput = function(config) {
39861     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39862 };
39863
39864 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39865         
39866         listWidth: undefined,
39867         
39868         selectedClass: 'active',
39869         
39870         invalidClass : "has-warning",
39871         
39872         validClass: 'has-success',
39873         
39874         allowed: '0123456789',
39875         
39876         /**
39877          * @cfg {String} defaultDialCode The default dial code when initializing the input
39878          */
39879         defaultDialCode: '+852',
39880         
39881         /**
39882          * @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
39883          */
39884         preferedCountries: false,
39885         
39886         getAutoCreate : function()
39887         {
39888             var data = Roo.bootstrap.PhoneInputData();
39889             var align = this.labelAlign || this.parentLabelAlign();
39890             var id = Roo.id();
39891             
39892             this.allCountries = [];
39893             this.dialCodeMapping = [];
39894             
39895             for (var i = 0; i < data.length; i++) {
39896               var c = data[i];
39897               this.allCountries[i] = {
39898                 name: c[0],
39899                 iso2: c[1],
39900                 dialCode: c[2],
39901                 priority: c[3] || 0,
39902                 areaCodes: c[4] || null
39903               };
39904               this.dialCodeMapping[c[2]] = {
39905                   name: c[0],
39906                   iso2: c[1],
39907                   priority: c[3] || 0,
39908                   areaCodes: c[4] || null
39909               };
39910             }
39911             
39912             var cfg = {
39913                 cls: 'form-group',
39914                 cn: []
39915             };
39916             
39917             var input =  {
39918                 tag: 'input',
39919                 id : id,
39920                 cls : 'form-control tel-input',
39921                 autocomplete: 'new-password'
39922             };
39923             
39924             var hiddenInput = {
39925                 tag: 'input',
39926                 type: 'hidden',
39927                 cls: 'hidden-tel-input'
39928             };
39929             
39930             if (this.name) {
39931                 hiddenInput.name = this.name;
39932             }
39933             
39934             if (this.disabled) {
39935                 input.disabled = true;
39936             }
39937             
39938             var flag_container = {
39939                 tag: 'div',
39940                 cls: 'flag-box',
39941                 cn: [
39942                     {
39943                         tag: 'div',
39944                         cls: 'flag'
39945                     },
39946                     {
39947                         tag: 'div',
39948                         cls: 'caret'
39949                     }
39950                 ]
39951             };
39952             
39953             var box = {
39954                 tag: 'div',
39955                 cls: this.hasFeedback ? 'has-feedback' : '',
39956                 cn: [
39957                     hiddenInput,
39958                     input,
39959                     {
39960                         tag: 'input',
39961                         cls: 'dial-code-holder',
39962                         disabled: true
39963                     }
39964                 ]
39965             };
39966             
39967             var container = {
39968                 cls: 'roo-select2-container input-group',
39969                 cn: [
39970                     flag_container,
39971                     box
39972                 ]
39973             };
39974             
39975             if (this.fieldLabel.length) {
39976                 var indicator = {
39977                     tag: 'i',
39978                     tooltip: 'This field is required'
39979                 };
39980                 
39981                 var label = {
39982                     tag: 'label',
39983                     'for':  id,
39984                     cls: 'control-label',
39985                     cn: []
39986                 };
39987                 
39988                 var label_text = {
39989                     tag: 'span',
39990                     html: this.fieldLabel
39991                 };
39992                 
39993                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39994                 label.cn = [
39995                     indicator,
39996                     label_text
39997                 ];
39998                 
39999                 if(this.indicatorpos == 'right') {
40000                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40001                     label.cn = [
40002                         label_text,
40003                         indicator
40004                     ];
40005                 }
40006                 
40007                 if(align == 'left') {
40008                     container = {
40009                         tag: 'div',
40010                         cn: [
40011                             container
40012                         ]
40013                     };
40014                     
40015                     if(this.labelWidth > 12){
40016                         label.style = "width: " + this.labelWidth + 'px';
40017                     }
40018                     if(this.labelWidth < 13 && this.labelmd == 0){
40019                         this.labelmd = this.labelWidth;
40020                     }
40021                     if(this.labellg > 0){
40022                         label.cls += ' col-lg-' + this.labellg;
40023                         input.cls += ' col-lg-' + (12 - this.labellg);
40024                     }
40025                     if(this.labelmd > 0){
40026                         label.cls += ' col-md-' + this.labelmd;
40027                         container.cls += ' col-md-' + (12 - this.labelmd);
40028                     }
40029                     if(this.labelsm > 0){
40030                         label.cls += ' col-sm-' + this.labelsm;
40031                         container.cls += ' col-sm-' + (12 - this.labelsm);
40032                     }
40033                     if(this.labelxs > 0){
40034                         label.cls += ' col-xs-' + this.labelxs;
40035                         container.cls += ' col-xs-' + (12 - this.labelxs);
40036                     }
40037                 }
40038             }
40039             
40040             cfg.cn = [
40041                 label,
40042                 container
40043             ];
40044             
40045             var settings = this;
40046             
40047             ['xs','sm','md','lg'].map(function(size){
40048                 if (settings[size]) {
40049                     cfg.cls += ' col-' + size + '-' + settings[size];
40050                 }
40051             });
40052             
40053             this.store = new Roo.data.Store({
40054                 proxy : new Roo.data.MemoryProxy({}),
40055                 reader : new Roo.data.JsonReader({
40056                     fields : [
40057                         {
40058                             'name' : 'name',
40059                             'type' : 'string'
40060                         },
40061                         {
40062                             'name' : 'iso2',
40063                             'type' : 'string'
40064                         },
40065                         {
40066                             'name' : 'dialCode',
40067                             'type' : 'string'
40068                         },
40069                         {
40070                             'name' : 'priority',
40071                             'type' : 'string'
40072                         },
40073                         {
40074                             'name' : 'areaCodes',
40075                             'type' : 'string'
40076                         }
40077                     ]
40078                 })
40079             });
40080             
40081             if(!this.preferedCountries) {
40082                 this.preferedCountries = [
40083                     'hk',
40084                     'gb',
40085                     'us'
40086                 ];
40087             }
40088             
40089             var p = this.preferedCountries.reverse();
40090             
40091             if(p) {
40092                 for (var i = 0; i < p.length; i++) {
40093                     for (var j = 0; j < this.allCountries.length; j++) {
40094                         if(this.allCountries[j].iso2 == p[i]) {
40095                             var t = this.allCountries[j];
40096                             this.allCountries.splice(j,1);
40097                             this.allCountries.unshift(t);
40098                         }
40099                     } 
40100                 }
40101             }
40102             
40103             this.store.proxy.data = {
40104                 success: true,
40105                 data: this.allCountries
40106             };
40107             
40108             return cfg;
40109         },
40110         
40111         initEvents : function()
40112         {
40113             this.createList();
40114             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40115             
40116             this.indicator = this.indicatorEl();
40117             this.flag = this.flagEl();
40118             this.dialCodeHolder = this.dialCodeHolderEl();
40119             
40120             this.trigger = this.el.select('div.flag-box',true).first();
40121             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40122             
40123             var _this = this;
40124             
40125             (function(){
40126                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40127                 _this.list.setWidth(lw);
40128             }).defer(100);
40129             
40130             this.list.on('mouseover', this.onViewOver, this);
40131             this.list.on('mousemove', this.onViewMove, this);
40132             this.inputEl().on("keyup", this.onKeyUp, this);
40133             
40134             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40135
40136             this.view = new Roo.View(this.list, this.tpl, {
40137                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40138             });
40139             
40140             this.view.on('click', this.onViewClick, this);
40141             this.setValue(this.defaultDialCode);
40142         },
40143         
40144         onTriggerClick : function(e)
40145         {
40146             Roo.log('trigger click');
40147             if(this.disabled){
40148                 return;
40149             }
40150             
40151             if(this.isExpanded()){
40152                 this.collapse();
40153                 this.hasFocus = false;
40154             }else {
40155                 this.store.load({});
40156                 this.hasFocus = true;
40157                 this.expand();
40158             }
40159         },
40160         
40161         isExpanded : function()
40162         {
40163             return this.list.isVisible();
40164         },
40165         
40166         collapse : function()
40167         {
40168             if(!this.isExpanded()){
40169                 return;
40170             }
40171             this.list.hide();
40172             Roo.get(document).un('mousedown', this.collapseIf, this);
40173             Roo.get(document).un('mousewheel', this.collapseIf, this);
40174             this.fireEvent('collapse', this);
40175             this.validate();
40176         },
40177         
40178         expand : function()
40179         {
40180             Roo.log('expand');
40181
40182             if(this.isExpanded() || !this.hasFocus){
40183                 return;
40184             }
40185             
40186             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40187             this.list.setWidth(lw);
40188             
40189             this.list.show();
40190             this.restrictHeight();
40191             
40192             Roo.get(document).on('mousedown', this.collapseIf, this);
40193             Roo.get(document).on('mousewheel', this.collapseIf, this);
40194             
40195             this.fireEvent('expand', this);
40196         },
40197         
40198         restrictHeight : function()
40199         {
40200             this.list.alignTo(this.inputEl(), this.listAlign);
40201             this.list.alignTo(this.inputEl(), this.listAlign);
40202         },
40203         
40204         onViewOver : function(e, t)
40205         {
40206             if(this.inKeyMode){
40207                 return;
40208             }
40209             var item = this.view.findItemFromChild(t);
40210             
40211             if(item){
40212                 var index = this.view.indexOf(item);
40213                 this.select(index, false);
40214             }
40215         },
40216
40217         // private
40218         onViewClick : function(view, doFocus, el, e)
40219         {
40220             var index = this.view.getSelectedIndexes()[0];
40221             
40222             var r = this.store.getAt(index);
40223             
40224             if(r){
40225                 this.onSelect(r, index);
40226             }
40227             if(doFocus !== false && !this.blockFocus){
40228                 this.inputEl().focus();
40229             }
40230         },
40231         
40232         onViewMove : function(e, t)
40233         {
40234             this.inKeyMode = false;
40235         },
40236         
40237         select : function(index, scrollIntoView)
40238         {
40239             this.selectedIndex = index;
40240             this.view.select(index);
40241             if(scrollIntoView !== false){
40242                 var el = this.view.getNode(index);
40243                 if(el){
40244                     this.list.scrollChildIntoView(el, false);
40245                 }
40246             }
40247         },
40248         
40249         createList : function()
40250         {
40251             this.list = Roo.get(document.body).createChild({
40252                 tag: 'ul',
40253                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40254                 style: 'display:none'
40255             });
40256             
40257             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40258         },
40259         
40260         collapseIf : function(e)
40261         {
40262             var in_combo  = e.within(this.el);
40263             var in_list =  e.within(this.list);
40264             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40265             
40266             if (in_combo || in_list || is_list) {
40267                 return;
40268             }
40269             this.collapse();
40270         },
40271         
40272         onSelect : function(record, index)
40273         {
40274             if(this.fireEvent('beforeselect', this, record, index) !== false){
40275                 
40276                 this.setFlagClass(record.data.iso2);
40277                 this.setDialCode(record.data.dialCode);
40278                 this.hasFocus = false;
40279                 this.collapse();
40280                 this.fireEvent('select', this, record, index);
40281             }
40282         },
40283         
40284         flagEl : function()
40285         {
40286             var flag = this.el.select('div.flag',true).first();
40287             if(!flag){
40288                 return false;
40289             }
40290             return flag;
40291         },
40292         
40293         dialCodeHolderEl : function()
40294         {
40295             var d = this.el.select('input.dial-code-holder',true).first();
40296             if(!d){
40297                 return false;
40298             }
40299             return d;
40300         },
40301         
40302         setDialCode : function(v)
40303         {
40304             this.dialCodeHolder.dom.value = '+'+v;
40305         },
40306         
40307         setFlagClass : function(n)
40308         {
40309             this.flag.dom.className = 'flag '+n;
40310         },
40311         
40312         getValue : function()
40313         {
40314             var v = this.inputEl().getValue();
40315             if(this.dialCodeHolder) {
40316                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40317             }
40318             return v;
40319         },
40320         
40321         setValue : function(v)
40322         {
40323             var d = this.getDialCode(v);
40324             
40325             //invalid dial code
40326             if(v.length == 0 || !d || d.length == 0) {
40327                 if(this.rendered){
40328                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40329                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40330                 }
40331                 return;
40332             }
40333             
40334             //valid dial code
40335             this.setFlagClass(this.dialCodeMapping[d].iso2);
40336             this.setDialCode(d);
40337             this.inputEl().dom.value = v.replace('+'+d,'');
40338             this.hiddenEl().dom.value = this.getValue();
40339             
40340             this.validate();
40341         },
40342         
40343         getDialCode : function(v)
40344         {
40345             v = v ||  '';
40346             
40347             if (v.length == 0) {
40348                 return this.dialCodeHolder.dom.value;
40349             }
40350             
40351             var dialCode = "";
40352             if (v.charAt(0) != "+") {
40353                 return false;
40354             }
40355             var numericChars = "";
40356             for (var i = 1; i < v.length; i++) {
40357               var c = v.charAt(i);
40358               if (!isNaN(c)) {
40359                 numericChars += c;
40360                 if (this.dialCodeMapping[numericChars]) {
40361                   dialCode = v.substr(1, i);
40362                 }
40363                 if (numericChars.length == 4) {
40364                   break;
40365                 }
40366               }
40367             }
40368             return dialCode;
40369         },
40370         
40371         reset : function()
40372         {
40373             this.setValue(this.defaultDialCode);
40374             this.validate();
40375         },
40376         
40377         hiddenEl : function()
40378         {
40379             return this.el.select('input.hidden-tel-input',true).first();
40380         },
40381         
40382         onKeyUp : function(e){
40383             
40384             var k = e.getKey();
40385             var c = e.getCharCode();
40386             
40387             if(
40388                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40389                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40390             ){
40391                 e.stopEvent();
40392             }
40393             
40394             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40395             //     return;
40396             // }
40397             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40398                 e.stopEvent();
40399             }
40400             
40401             this.setValue(this.getValue());
40402         }
40403         
40404 });
40405 /**
40406  * @class Roo.bootstrap.MoneyField
40407  * @extends Roo.bootstrap.ComboBox
40408  * Bootstrap MoneyField class
40409  * 
40410  * @constructor
40411  * Create a new MoneyField.
40412  * @param {Object} config Configuration options
40413  */
40414
40415 Roo.bootstrap.MoneyField = function(config) {
40416     
40417     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40418     
40419 };
40420
40421 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40422     
40423     /**
40424      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40425      */
40426     allowDecimals : true,
40427     /**
40428      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40429      */
40430     decimalSeparator : ".",
40431     /**
40432      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40433      */
40434     decimalPrecision : 0,
40435     /**
40436      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40437      */
40438     allowNegative : true,
40439     /**
40440      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40441      */
40442     allowZero: true,
40443     /**
40444      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40445      */
40446     minValue : Number.NEGATIVE_INFINITY,
40447     /**
40448      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40449      */
40450     maxValue : Number.MAX_VALUE,
40451     /**
40452      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40453      */
40454     minText : "The minimum value for this field is {0}",
40455     /**
40456      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40457      */
40458     maxText : "The maximum value for this field is {0}",
40459     /**
40460      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40461      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40462      */
40463     nanText : "{0} is not a valid number",
40464     /**
40465      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40466      */
40467     castInt : true,
40468     /**
40469      * @cfg {String} defaults currency of the MoneyField
40470      * value should be in lkey
40471      */
40472     defaultCurrency : false,
40473     /**
40474      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40475      */
40476     thousandsDelimiter : false,
40477     
40478     
40479     inputlg : 9,
40480     inputmd : 9,
40481     inputsm : 9,
40482     inputxs : 6,
40483     
40484     store : false,
40485     
40486     getAutoCreate : function()
40487     {
40488         var align = this.labelAlign || this.parentLabelAlign();
40489         
40490         var id = Roo.id();
40491
40492         var cfg = {
40493             cls: 'form-group',
40494             cn: []
40495         };
40496
40497         var input =  {
40498             tag: 'input',
40499             id : id,
40500             cls : 'form-control roo-money-amount-input',
40501             autocomplete: 'new-password'
40502         };
40503         
40504         var hiddenInput = {
40505             tag: 'input',
40506             type: 'hidden',
40507             id: Roo.id(),
40508             cls: 'hidden-number-input'
40509         };
40510         
40511         if (this.name) {
40512             hiddenInput.name = this.name;
40513         }
40514
40515         if (this.disabled) {
40516             input.disabled = true;
40517         }
40518
40519         var clg = 12 - this.inputlg;
40520         var cmd = 12 - this.inputmd;
40521         var csm = 12 - this.inputsm;
40522         var cxs = 12 - this.inputxs;
40523         
40524         var container = {
40525             tag : 'div',
40526             cls : 'row roo-money-field',
40527             cn : [
40528                 {
40529                     tag : 'div',
40530                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40531                     cn : [
40532                         {
40533                             tag : 'div',
40534                             cls: 'roo-select2-container input-group',
40535                             cn: [
40536                                 {
40537                                     tag : 'input',
40538                                     cls : 'form-control roo-money-currency-input',
40539                                     autocomplete: 'new-password',
40540                                     readOnly : 1,
40541                                     name : this.currencyName
40542                                 },
40543                                 {
40544                                     tag :'span',
40545                                     cls : 'input-group-addon',
40546                                     cn : [
40547                                         {
40548                                             tag: 'span',
40549                                             cls: 'caret'
40550                                         }
40551                                     ]
40552                                 }
40553                             ]
40554                         }
40555                     ]
40556                 },
40557                 {
40558                     tag : 'div',
40559                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40560                     cn : [
40561                         {
40562                             tag: 'div',
40563                             cls: this.hasFeedback ? 'has-feedback' : '',
40564                             cn: [
40565                                 input
40566                             ]
40567                         }
40568                     ]
40569                 }
40570             ]
40571             
40572         };
40573         
40574         if (this.fieldLabel.length) {
40575             var indicator = {
40576                 tag: 'i',
40577                 tooltip: 'This field is required'
40578             };
40579
40580             var label = {
40581                 tag: 'label',
40582                 'for':  id,
40583                 cls: 'control-label',
40584                 cn: []
40585             };
40586
40587             var label_text = {
40588                 tag: 'span',
40589                 html: this.fieldLabel
40590             };
40591
40592             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40593             label.cn = [
40594                 indicator,
40595                 label_text
40596             ];
40597
40598             if(this.indicatorpos == 'right') {
40599                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40600                 label.cn = [
40601                     label_text,
40602                     indicator
40603                 ];
40604             }
40605
40606             if(align == 'left') {
40607                 container = {
40608                     tag: 'div',
40609                     cn: [
40610                         container
40611                     ]
40612                 };
40613
40614                 if(this.labelWidth > 12){
40615                     label.style = "width: " + this.labelWidth + 'px';
40616                 }
40617                 if(this.labelWidth < 13 && this.labelmd == 0){
40618                     this.labelmd = this.labelWidth;
40619                 }
40620                 if(this.labellg > 0){
40621                     label.cls += ' col-lg-' + this.labellg;
40622                     input.cls += ' col-lg-' + (12 - this.labellg);
40623                 }
40624                 if(this.labelmd > 0){
40625                     label.cls += ' col-md-' + this.labelmd;
40626                     container.cls += ' col-md-' + (12 - this.labelmd);
40627                 }
40628                 if(this.labelsm > 0){
40629                     label.cls += ' col-sm-' + this.labelsm;
40630                     container.cls += ' col-sm-' + (12 - this.labelsm);
40631                 }
40632                 if(this.labelxs > 0){
40633                     label.cls += ' col-xs-' + this.labelxs;
40634                     container.cls += ' col-xs-' + (12 - this.labelxs);
40635                 }
40636             }
40637         }
40638
40639         cfg.cn = [
40640             label,
40641             container,
40642             hiddenInput
40643         ];
40644         
40645         var settings = this;
40646
40647         ['xs','sm','md','lg'].map(function(size){
40648             if (settings[size]) {
40649                 cfg.cls += ' col-' + size + '-' + settings[size];
40650             }
40651         });
40652         
40653         return cfg;
40654     },
40655     
40656     initEvents : function()
40657     {
40658         this.indicator = this.indicatorEl();
40659         
40660         this.initCurrencyEvent();
40661         
40662         this.initNumberEvent();
40663     },
40664     
40665     initCurrencyEvent : function()
40666     {
40667         if (!this.store) {
40668             throw "can not find store for combo";
40669         }
40670         
40671         this.store = Roo.factory(this.store, Roo.data);
40672         this.store.parent = this;
40673         
40674         this.createList();
40675         
40676         this.triggerEl = this.el.select('.input-group-addon', true).first();
40677         
40678         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40679         
40680         var _this = this;
40681         
40682         (function(){
40683             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40684             _this.list.setWidth(lw);
40685         }).defer(100);
40686         
40687         this.list.on('mouseover', this.onViewOver, this);
40688         this.list.on('mousemove', this.onViewMove, this);
40689         this.list.on('scroll', this.onViewScroll, this);
40690         
40691         if(!this.tpl){
40692             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40693         }
40694         
40695         this.view = new Roo.View(this.list, this.tpl, {
40696             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40697         });
40698         
40699         this.view.on('click', this.onViewClick, this);
40700         
40701         this.store.on('beforeload', this.onBeforeLoad, this);
40702         this.store.on('load', this.onLoad, this);
40703         this.store.on('loadexception', this.onLoadException, this);
40704         
40705         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40706             "up" : function(e){
40707                 this.inKeyMode = true;
40708                 this.selectPrev();
40709             },
40710
40711             "down" : function(e){
40712                 if(!this.isExpanded()){
40713                     this.onTriggerClick();
40714                 }else{
40715                     this.inKeyMode = true;
40716                     this.selectNext();
40717                 }
40718             },
40719
40720             "enter" : function(e){
40721                 this.collapse();
40722                 
40723                 if(this.fireEvent("specialkey", this, e)){
40724                     this.onViewClick(false);
40725                 }
40726                 
40727                 return true;
40728             },
40729
40730             "esc" : function(e){
40731                 this.collapse();
40732             },
40733
40734             "tab" : function(e){
40735                 this.collapse();
40736                 
40737                 if(this.fireEvent("specialkey", this, e)){
40738                     this.onViewClick(false);
40739                 }
40740                 
40741                 return true;
40742             },
40743
40744             scope : this,
40745
40746             doRelay : function(foo, bar, hname){
40747                 if(hname == 'down' || this.scope.isExpanded()){
40748                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40749                 }
40750                 return true;
40751             },
40752
40753             forceKeyDown: true
40754         });
40755         
40756         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40757         
40758     },
40759     
40760     initNumberEvent : function(e)
40761     {
40762         this.inputEl().on("keydown" , this.fireKey,  this);
40763         this.inputEl().on("focus", this.onFocus,  this);
40764         this.inputEl().on("blur", this.onBlur,  this);
40765         
40766         this.inputEl().relayEvent('keyup', this);
40767         
40768         if(this.indicator){
40769             this.indicator.addClass('invisible');
40770         }
40771  
40772         this.originalValue = this.getValue();
40773         
40774         if(this.validationEvent == 'keyup'){
40775             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40776             this.inputEl().on('keyup', this.filterValidation, this);
40777         }
40778         else if(this.validationEvent !== false){
40779             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40780         }
40781         
40782         if(this.selectOnFocus){
40783             this.on("focus", this.preFocus, this);
40784             
40785         }
40786         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40787             this.inputEl().on("keypress", this.filterKeys, this);
40788         } else {
40789             this.inputEl().relayEvent('keypress', this);
40790         }
40791         
40792         var allowed = "0123456789";
40793         
40794         if(this.allowDecimals){
40795             allowed += this.decimalSeparator;
40796         }
40797         
40798         if(this.allowNegative){
40799             allowed += "-";
40800         }
40801         
40802         if(this.thousandsDelimiter) {
40803             allowed += ",";
40804         }
40805         
40806         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40807         
40808         var keyPress = function(e){
40809             
40810             var k = e.getKey();
40811             
40812             var c = e.getCharCode();
40813             
40814             if(
40815                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40816                     allowed.indexOf(String.fromCharCode(c)) === -1
40817             ){
40818                 e.stopEvent();
40819                 return;
40820             }
40821             
40822             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40823                 return;
40824             }
40825             
40826             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40827                 e.stopEvent();
40828             }
40829         };
40830         
40831         this.inputEl().on("keypress", keyPress, this);
40832         
40833     },
40834     
40835     onTriggerClick : function(e)
40836     {   
40837         if(this.disabled){
40838             return;
40839         }
40840         
40841         this.page = 0;
40842         this.loadNext = false;
40843         
40844         if(this.isExpanded()){
40845             this.collapse();
40846             return;
40847         }
40848         
40849         this.hasFocus = true;
40850         
40851         if(this.triggerAction == 'all') {
40852             this.doQuery(this.allQuery, true);
40853             return;
40854         }
40855         
40856         this.doQuery(this.getRawValue());
40857     },
40858     
40859     getCurrency : function()
40860     {   
40861         var v = this.currencyEl().getValue();
40862         
40863         return v;
40864     },
40865     
40866     restrictHeight : function()
40867     {
40868         this.list.alignTo(this.currencyEl(), this.listAlign);
40869         this.list.alignTo(this.currencyEl(), this.listAlign);
40870     },
40871     
40872     onViewClick : function(view, doFocus, el, e)
40873     {
40874         var index = this.view.getSelectedIndexes()[0];
40875         
40876         var r = this.store.getAt(index);
40877         
40878         if(r){
40879             this.onSelect(r, index);
40880         }
40881     },
40882     
40883     onSelect : function(record, index){
40884         
40885         if(this.fireEvent('beforeselect', this, record, index) !== false){
40886         
40887             this.setFromCurrencyData(index > -1 ? record.data : false);
40888             
40889             this.collapse();
40890             
40891             this.fireEvent('select', this, record, index);
40892         }
40893     },
40894     
40895     setFromCurrencyData : function(o)
40896     {
40897         var currency = '';
40898         
40899         this.lastCurrency = o;
40900         
40901         if (this.currencyField) {
40902             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40903         } else {
40904             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40905         }
40906         
40907         this.lastSelectionText = currency;
40908         
40909         //setting default currency
40910         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40911             this.setCurrency(this.defaultCurrency);
40912             return;
40913         }
40914         
40915         this.setCurrency(currency);
40916     },
40917     
40918     setFromData : function(o)
40919     {
40920         var c = {};
40921         
40922         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40923         
40924         this.setFromCurrencyData(c);
40925         
40926         var value = '';
40927         
40928         if (this.name) {
40929             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40930         } else {
40931             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40932         }
40933         
40934         this.setValue(value);
40935         
40936     },
40937     
40938     setCurrency : function(v)
40939     {   
40940         this.currencyValue = v;
40941         
40942         if(this.rendered){
40943             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40944             this.validate();
40945         }
40946     },
40947     
40948     setValue : function(v)
40949     {
40950         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40951         
40952         this.value = v;
40953         
40954         if(this.rendered){
40955             
40956             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40957             
40958             this.inputEl().dom.value = (v == '') ? '' :
40959                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40960             
40961             if(!this.allowZero && v === '0') {
40962                 this.hiddenEl().dom.value = '';
40963                 this.inputEl().dom.value = '';
40964             }
40965             
40966             this.validate();
40967         }
40968     },
40969     
40970     getRawValue : function()
40971     {
40972         var v = this.inputEl().getValue();
40973         
40974         return v;
40975     },
40976     
40977     getValue : function()
40978     {
40979         return this.fixPrecision(this.parseValue(this.getRawValue()));
40980     },
40981     
40982     parseValue : function(value)
40983     {
40984         if(this.thousandsDelimiter) {
40985             value += "";
40986             r = new RegExp(",", "g");
40987             value = value.replace(r, "");
40988         }
40989         
40990         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40991         return isNaN(value) ? '' : value;
40992         
40993     },
40994     
40995     fixPrecision : function(value)
40996     {
40997         if(this.thousandsDelimiter) {
40998             value += "";
40999             r = new RegExp(",", "g");
41000             value = value.replace(r, "");
41001         }
41002         
41003         var nan = isNaN(value);
41004         
41005         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41006             return nan ? '' : value;
41007         }
41008         return parseFloat(value).toFixed(this.decimalPrecision);
41009     },
41010     
41011     decimalPrecisionFcn : function(v)
41012     {
41013         return Math.floor(v);
41014     },
41015     
41016     validateValue : function(value)
41017     {
41018         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41019             return false;
41020         }
41021         
41022         var num = this.parseValue(value);
41023         
41024         if(isNaN(num)){
41025             this.markInvalid(String.format(this.nanText, value));
41026             return false;
41027         }
41028         
41029         if(num < this.minValue){
41030             this.markInvalid(String.format(this.minText, this.minValue));
41031             return false;
41032         }
41033         
41034         if(num > this.maxValue){
41035             this.markInvalid(String.format(this.maxText, this.maxValue));
41036             return false;
41037         }
41038         
41039         return true;
41040     },
41041     
41042     validate : function()
41043     {
41044         if(this.disabled || this.allowBlank){
41045             this.markValid();
41046             return true;
41047         }
41048         
41049         var currency = this.getCurrency();
41050         
41051         if(this.validateValue(this.getRawValue()) && currency.length){
41052             this.markValid();
41053             return true;
41054         }
41055         
41056         this.markInvalid();
41057         return false;
41058     },
41059     
41060     getName: function()
41061     {
41062         return this.name;
41063     },
41064     
41065     beforeBlur : function()
41066     {
41067         if(!this.castInt){
41068             return;
41069         }
41070         
41071         var v = this.parseValue(this.getRawValue());
41072         
41073         if(v || v == 0){
41074             this.setValue(v);
41075         }
41076     },
41077     
41078     onBlur : function()
41079     {
41080         this.beforeBlur();
41081         
41082         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41083             //this.el.removeClass(this.focusClass);
41084         }
41085         
41086         this.hasFocus = false;
41087         
41088         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41089             this.validate();
41090         }
41091         
41092         var v = this.getValue();
41093         
41094         if(String(v) !== String(this.startValue)){
41095             this.fireEvent('change', this, v, this.startValue);
41096         }
41097         
41098         this.fireEvent("blur", this);
41099     },
41100     
41101     inputEl : function()
41102     {
41103         return this.el.select('.roo-money-amount-input', true).first();
41104     },
41105     
41106     currencyEl : function()
41107     {
41108         return this.el.select('.roo-money-currency-input', true).first();
41109     },
41110     
41111     hiddenEl : function()
41112     {
41113         return this.el.select('input.hidden-number-input',true).first();
41114     }
41115     
41116 });