roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
921     setWeight : function(str)
922     {
923         this.el.removeClass(this.weightClass);
924         this.el.addClass('btn-' + str);        
925     }
926     
927     
928 });
929
930  /*
931  * - LGPL
932  *
933  * column
934  * 
935  */
936
937 /**
938  * @class Roo.bootstrap.Column
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Column class
941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
949  *
950  * 
951  * @cfg {Boolean} hidden (true|false) hide the element
952  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953  * @cfg {String} fa (ban|check|...) font awesome icon
954  * @cfg {Number} fasize (1|2|....) font awsome size
955
956  * @cfg {String} icon (info-sign|check|...) glyphicon name
957
958  * @cfg {String} html content of column.
959  * 
960  * @constructor
961  * Create a new Column
962  * @param {Object} config The config object
963  */
964
965 Roo.bootstrap.Column = function(config){
966     Roo.bootstrap.Column.superclass.constructor.call(this, config);
967 };
968
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
970     
971     xs: false,
972     sm: false,
973     md: false,
974     lg: false,
975     xsoff: false,
976     smoff: false,
977     mdoff: false,
978     lgoff: false,
979     html: '',
980     offset: 0,
981     alert: false,
982     fa: false,
983     icon : false,
984     hidden : false,
985     fasize : 1,
986     
987     getAutoCreate : function(){
988         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
989         
990         cfg = {
991             tag: 'div',
992             cls: 'column'
993         };
994         
995         var settings=this;
996         ['xs','sm','md','lg'].map(function(size){
997             //Roo.log( size + ':' + settings[size]);
998             
999             if (settings[size+'off'] !== false) {
1000                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1001             }
1002             
1003             if (settings[size] === false) {
1004                 return;
1005             }
1006             
1007             if (!settings[size]) { // 0 = hidden
1008                 cfg.cls += ' hidden-' + size;
1009                 return;
1010             }
1011             cfg.cls += ' col-' + size + '-' + settings[size];
1012             
1013         });
1014         
1015         if (this.hidden) {
1016             cfg.cls += ' hidden';
1017         }
1018         
1019         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020             cfg.cls +=' alert alert-' + this.alert;
1021         }
1022         
1023         
1024         if (this.html.length) {
1025             cfg.html = this.html;
1026         }
1027         if (this.fa) {
1028             var fasize = '';
1029             if (this.fasize > 1) {
1030                 fasize = ' fa-' + this.fasize + 'x';
1031             }
1032             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1033             
1034             
1035         }
1036         if (this.icon) {
1037             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1038         }
1039         
1040         return cfg;
1041     }
1042    
1043 });
1044
1045  
1046
1047  /*
1048  * - LGPL
1049  *
1050  * page container.
1051  * 
1052  */
1053
1054
1055 /**
1056  * @class Roo.bootstrap.Container
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Container class
1059  * @cfg {Boolean} jumbotron is it a jumbotron element
1060  * @cfg {String} html content of element
1061  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1063  * @cfg {String} header content of header (for panel)
1064  * @cfg {String} footer content of footer (for panel)
1065  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066  * @cfg {String} tag (header|aside|section) type of HTML tag.
1067  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068  * @cfg {String} fa font awesome icon
1069  * @cfg {String} icon (info-sign|check|...) glyphicon name
1070  * @cfg {Boolean} hidden (true|false) hide the element
1071  * @cfg {Boolean} expandable (true|false) default false
1072  * @cfg {Boolean} expanded (true|false) default true
1073  * @cfg {String} rheader contet on the right of header
1074  * @cfg {Boolean} clickable (true|false) default false
1075
1076  *     
1077  * @constructor
1078  * Create a new Container
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Container = function(config){
1083     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // raw events
1087          /**
1088          * @event expand
1089          * After the panel has been expand
1090          * 
1091          * @param {Roo.bootstrap.Container} this
1092          */
1093         "expand" : true,
1094         /**
1095          * @event collapse
1096          * After the panel has been collapsed
1097          * 
1098          * @param {Roo.bootstrap.Container} this
1099          */
1100         "collapse" : true,
1101         /**
1102          * @event click
1103          * When a element is chick
1104          * @param {Roo.bootstrap.Container} this
1105          * @param {Roo.EventObject} e
1106          */
1107         "click" : true
1108     });
1109 };
1110
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1112     
1113     jumbotron : false,
1114     well: '',
1115     panel : '',
1116     header: '',
1117     footer : '',
1118     sticky: '',
1119     tag : false,
1120     alert : false,
1121     fa: false,
1122     icon : false,
1123     expandable : false,
1124     rheader : '',
1125     expanded : true,
1126     clickable: false,
1127   
1128      
1129     getChildContainer : function() {
1130         
1131         if(!this.el){
1132             return false;
1133         }
1134         
1135         if (this.panel.length) {
1136             return this.el.select('.panel-body',true).first();
1137         }
1138         
1139         return this.el;
1140     },
1141     
1142     
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag : this.tag || 'div',
1147             html : '',
1148             cls : ''
1149         };
1150         if (this.jumbotron) {
1151             cfg.cls = 'jumbotron';
1152         }
1153         
1154         
1155         
1156         // - this is applied by the parent..
1157         //if (this.cls) {
1158         //    cfg.cls = this.cls + '';
1159         //}
1160         
1161         if (this.sticky.length) {
1162             
1163             var bd = Roo.get(document.body);
1164             if (!bd.hasClass('bootstrap-sticky')) {
1165                 bd.addClass('bootstrap-sticky');
1166                 Roo.select('html',true).setStyle('height', '100%');
1167             }
1168              
1169             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1170         }
1171         
1172         
1173         if (this.well.length) {
1174             switch (this.well) {
1175                 case 'lg':
1176                 case 'sm':
1177                     cfg.cls +=' well well-' +this.well;
1178                     break;
1179                 default:
1180                     cfg.cls +=' well';
1181                     break;
1182             }
1183         }
1184         
1185         if (this.hidden) {
1186             cfg.cls += ' hidden';
1187         }
1188         
1189         
1190         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191             cfg.cls +=' alert alert-' + this.alert;
1192         }
1193         
1194         var body = cfg;
1195         
1196         if (this.panel.length) {
1197             cfg.cls += ' panel panel-' + this.panel;
1198             cfg.cn = [];
1199             if (this.header.length) {
1200                 
1201                 var h = [];
1202                 
1203                 if(this.expandable){
1204                     
1205                     cfg.cls = cfg.cls + ' expandable';
1206                     
1207                     h.push({
1208                         tag: 'i',
1209                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1210                     });
1211                     
1212                 }
1213                 
1214                 h.push(
1215                     {
1216                         tag: 'span',
1217                         cls : 'panel-title',
1218                         html : (this.expandable ? '&nbsp;' : '') + this.header
1219                     },
1220                     {
1221                         tag: 'span',
1222                         cls: 'panel-header-right',
1223                         html: this.rheader
1224                     }
1225                 );
1226                 
1227                 cfg.cn.push({
1228                     cls : 'panel-heading',
1229                     style : this.expandable ? 'cursor: pointer' : '',
1230                     cn : h
1231                 });
1232                 
1233             }
1234             
1235             body = false;
1236             cfg.cn.push({
1237                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1238                 html : this.html
1239             });
1240             
1241             
1242             if (this.footer.length) {
1243                 cfg.cn.push({
1244                     cls : 'panel-footer',
1245                     html : this.footer
1246                     
1247                 });
1248             }
1249             
1250         }
1251         
1252         if (body) {
1253             body.html = this.html || cfg.html;
1254             // prefix with the icons..
1255             if (this.fa) {
1256                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1257             }
1258             if (this.icon) {
1259                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1260             }
1261             
1262             
1263         }
1264         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265             cfg.cls =  'container';
1266         }
1267         
1268         return cfg;
1269     },
1270     
1271     initEvents: function() 
1272     {
1273         if(this.expandable){
1274             var headerEl = this.headerEl();
1275         
1276             if(headerEl){
1277                 headerEl.on('click', this.onToggleClick, this);
1278             }
1279         }
1280         
1281         if(this.clickable){
1282             this.el.on('click', this.onClick, this);
1283         }
1284         
1285     },
1286     
1287     onToggleClick : function()
1288     {
1289         var headerEl = this.headerEl();
1290         
1291         if(!headerEl){
1292             return;
1293         }
1294         
1295         if(this.expanded){
1296             this.collapse();
1297             return;
1298         }
1299         
1300         this.expand();
1301     },
1302     
1303     expand : function()
1304     {
1305         if(this.fireEvent('expand', this)) {
1306             
1307             this.expanded = true;
1308             
1309             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1310             
1311             this.el.select('.panel-body',true).first().removeClass('hide');
1312             
1313             var toggleEl = this.toggleEl();
1314
1315             if(!toggleEl){
1316                 return;
1317             }
1318
1319             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1320         }
1321         
1322     },
1323     
1324     collapse : function()
1325     {
1326         if(this.fireEvent('collapse', this)) {
1327             
1328             this.expanded = false;
1329             
1330             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331             this.el.select('.panel-body',true).first().addClass('hide');
1332         
1333             var toggleEl = this.toggleEl();
1334
1335             if(!toggleEl){
1336                 return;
1337             }
1338
1339             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1340         }
1341     },
1342     
1343     toggleEl : function()
1344     {
1345         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1346             return;
1347         }
1348         
1349         return this.el.select('.panel-heading .fa',true).first();
1350     },
1351     
1352     headerEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading',true).first()
1359     },
1360     
1361     bodyEl : function()
1362     {
1363         if(!this.el || !this.panel.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-body',true).first()
1368     },
1369     
1370     titleEl : function()
1371     {
1372         if(!this.el || !this.panel.length || !this.header.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-title',true).first();
1377     },
1378     
1379     setTitle : function(v)
1380     {
1381         var titleEl = this.titleEl();
1382         
1383         if(!titleEl){
1384             return;
1385         }
1386         
1387         titleEl.dom.innerHTML = v;
1388     },
1389     
1390     getTitle : function()
1391     {
1392         
1393         var titleEl = this.titleEl();
1394         
1395         if(!titleEl){
1396             return '';
1397         }
1398         
1399         return titleEl.dom.innerHTML;
1400     },
1401     
1402     setRightTitle : function(v)
1403     {
1404         var t = this.el.select('.panel-header-right',true).first();
1405         
1406         if(!t){
1407             return;
1408         }
1409         
1410         t.dom.innerHTML = v;
1411     },
1412     
1413     onClick : function(e)
1414     {
1415         e.preventDefault();
1416         
1417         this.fireEvent('click', this, e);
1418     }
1419 });
1420
1421  /*
1422  * - LGPL
1423  *
1424  * image
1425  * 
1426  */
1427
1428
1429 /**
1430  * @class Roo.bootstrap.Img
1431  * @extends Roo.bootstrap.Component
1432  * Bootstrap Img class
1433  * @cfg {Boolean} imgResponsive false | true
1434  * @cfg {String} border rounded | circle | thumbnail
1435  * @cfg {String} src image source
1436  * @cfg {String} alt image alternative text
1437  * @cfg {String} href a tag href
1438  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439  * @cfg {String} xsUrl xs image source
1440  * @cfg {String} smUrl sm image source
1441  * @cfg {String} mdUrl md image source
1442  * @cfg {String} lgUrl lg image source
1443  * 
1444  * @constructor
1445  * Create a new Input
1446  * @param {Object} config The config object
1447  */
1448
1449 Roo.bootstrap.Img = function(config){
1450     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451     
1452     this.addEvents({
1453         // img events
1454         /**
1455          * @event click
1456          * The img click event for the img.
1457          * @param {Roo.EventObject} e
1458          */
1459         "click" : true
1460     });
1461 };
1462
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1464     
1465     imgResponsive: true,
1466     border: '',
1467     src: 'about:blank',
1468     href: false,
1469     target: false,
1470     xsUrl: '',
1471     smUrl: '',
1472     mdUrl: '',
1473     lgUrl: '',
1474
1475     getAutoCreate : function()
1476     {   
1477         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478             return this.createSingleImg();
1479         }
1480         
1481         var cfg = {
1482             tag: 'div',
1483             cls: 'roo-image-responsive-group',
1484             cn: []
1485         };
1486         var _this = this;
1487         
1488         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1489             
1490             if(!_this[size + 'Url']){
1491                 return;
1492             }
1493             
1494             var img = {
1495                 tag: 'img',
1496                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497                 html: _this.html || cfg.html,
1498                 src: _this[size + 'Url']
1499             };
1500             
1501             img.cls += ' roo-image-responsive-' + size;
1502             
1503             var s = ['xs', 'sm', 'md', 'lg'];
1504             
1505             s.splice(s.indexOf(size), 1);
1506             
1507             Roo.each(s, function(ss){
1508                 img.cls += ' hidden-' + ss;
1509             });
1510             
1511             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512                 cfg.cls += ' img-' + _this.border;
1513             }
1514             
1515             if(_this.alt){
1516                 cfg.alt = _this.alt;
1517             }
1518             
1519             if(_this.href){
1520                 var a = {
1521                     tag: 'a',
1522                     href: _this.href,
1523                     cn: [
1524                         img
1525                     ]
1526                 };
1527
1528                 if(this.target){
1529                     a.target = _this.target;
1530                 }
1531             }
1532             
1533             cfg.cn.push((_this.href) ? a : img);
1534             
1535         });
1536         
1537         return cfg;
1538     },
1539     
1540     createSingleImg : function()
1541     {
1542         var cfg = {
1543             tag: 'img',
1544             cls: (this.imgResponsive) ? 'img-responsive' : '',
1545             html : null,
1546             src : 'about:blank'  // just incase src get's set to undefined?!?
1547         };
1548         
1549         cfg.html = this.html || cfg.html;
1550         
1551         cfg.src = this.src || cfg.src;
1552         
1553         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554             cfg.cls += ' img-' + this.border;
1555         }
1556         
1557         if(this.alt){
1558             cfg.alt = this.alt;
1559         }
1560         
1561         if(this.href){
1562             var a = {
1563                 tag: 'a',
1564                 href: this.href,
1565                 cn: [
1566                     cfg
1567                 ]
1568             };
1569             
1570             if(this.target){
1571                 a.target = this.target;
1572             }
1573             
1574         }
1575         
1576         return (this.href) ? a : cfg;
1577     },
1578     
1579     initEvents: function() 
1580     {
1581         if(!this.href){
1582             this.el.on('click', this.onClick, this);
1583         }
1584         
1585     },
1586     
1587     onClick : function(e)
1588     {
1589         Roo.log('img onclick');
1590         this.fireEvent('click', this, e);
1591     },
1592     /**
1593      * Sets the url of the image - used to update it
1594      * @param {String} url the url of the image
1595      */
1596     
1597     setSrc : function(url)
1598     {
1599         this.src =  url;
1600         
1601         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602             this.el.dom.src =  url;
1603             return;
1604         }
1605         
1606         this.el.select('img', true).first().dom.src =  url;
1607     }
1608     
1609     
1610    
1611 });
1612
1613  /*
1614  * - LGPL
1615  *
1616  * image
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.Link
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap Link Class
1625  * @cfg {String} alt image alternative text
1626  * @cfg {String} href a tag href
1627  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628  * @cfg {String} html the content of the link.
1629  * @cfg {String} anchor name for the anchor link
1630  * @cfg {String} fa - favicon
1631
1632  * @cfg {Boolean} preventDefault (true | false) default false
1633
1634  * 
1635  * @constructor
1636  * Create a new Input
1637  * @param {Object} config The config object
1638  */
1639
1640 Roo.bootstrap.Link = function(config){
1641     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642     
1643     this.addEvents({
1644         // img events
1645         /**
1646          * @event click
1647          * The img click event for the img.
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1655     
1656     href: false,
1657     target: false,
1658     preventDefault: false,
1659     anchor : false,
1660     alt : false,
1661     fa: false,
1662
1663
1664     getAutoCreate : function()
1665     {
1666         var html = this.html || '';
1667         
1668         if (this.fa !== false) {
1669             html = '<i class="fa fa-' + this.fa + '"></i>';
1670         }
1671         var cfg = {
1672             tag: 'a'
1673         };
1674         // anchor's do not require html/href...
1675         if (this.anchor === false) {
1676             cfg.html = html;
1677             cfg.href = this.href || '#';
1678         } else {
1679             cfg.name = this.anchor;
1680             if (this.html !== false || this.fa !== false) {
1681                 cfg.html = html;
1682             }
1683             if (this.href !== false) {
1684                 cfg.href = this.href;
1685             }
1686         }
1687         
1688         if(this.alt !== false){
1689             cfg.alt = this.alt;
1690         }
1691         
1692         
1693         if(this.target !== false) {
1694             cfg.target = this.target;
1695         }
1696         
1697         return cfg;
1698     },
1699     
1700     initEvents: function() {
1701         
1702         if(!this.href || this.preventDefault){
1703             this.el.on('click', this.onClick, this);
1704         }
1705     },
1706     
1707     onClick : function(e)
1708     {
1709         if(this.preventDefault){
1710             e.preventDefault();
1711         }
1712         //Roo.log('img onclick');
1713         this.fireEvent('click', this, e);
1714     }
1715    
1716 });
1717
1718  /*
1719  * - LGPL
1720  *
1721  * header
1722  * 
1723  */
1724
1725 /**
1726  * @class Roo.bootstrap.Header
1727  * @extends Roo.bootstrap.Component
1728  * Bootstrap Header class
1729  * @cfg {String} html content of header
1730  * @cfg {Number} level (1|2|3|4|5|6) default 1
1731  * 
1732  * @constructor
1733  * Create a new Header
1734  * @param {Object} config The config object
1735  */
1736
1737
1738 Roo.bootstrap.Header  = function(config){
1739     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1740 };
1741
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1743     
1744     //href : false,
1745     html : false,
1746     level : 1,
1747     
1748     
1749     
1750     getAutoCreate : function(){
1751         
1752         
1753         
1754         var cfg = {
1755             tag: 'h' + (1 *this.level),
1756             html: this.html || ''
1757         } ;
1758         
1759         return cfg;
1760     }
1761    
1762 });
1763
1764  
1765
1766  /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776  
1777 /**
1778  * @class Roo.bootstrap.MenuMgr
1779  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1780  * @singleton
1781  */
1782 Roo.bootstrap.MenuMgr = function(){
1783    var menus, active, groups = {}, attached = false, lastShow = new Date();
1784
1785    // private - called when first menu is created
1786    function init(){
1787        menus = {};
1788        active = new Roo.util.MixedCollection();
1789        Roo.get(document).addKeyListener(27, function(){
1790            if(active.length > 0){
1791                hideAll();
1792            }
1793        });
1794    }
1795
1796    // private
1797    function hideAll(){
1798        if(active && active.length > 0){
1799            var c = active.clone();
1800            c.each(function(m){
1801                m.hide();
1802            });
1803        }
1804    }
1805
1806    // private
1807    function onHide(m){
1808        active.remove(m);
1809        if(active.length < 1){
1810            Roo.get(document).un("mouseup", onMouseDown);
1811             
1812            attached = false;
1813        }
1814    }
1815
1816    // private
1817    function onShow(m){
1818        var last = active.last();
1819        lastShow = new Date();
1820        active.add(m);
1821        if(!attached){
1822           Roo.get(document).on("mouseup", onMouseDown);
1823            
1824            attached = true;
1825        }
1826        if(m.parentMenu){
1827           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828           m.parentMenu.activeChild = m;
1829        }else if(last && last.isVisible()){
1830           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1831        }
1832    }
1833
1834    // private
1835    function onBeforeHide(m){
1836        if(m.activeChild){
1837            m.activeChild.hide();
1838        }
1839        if(m.autoHideTimer){
1840            clearTimeout(m.autoHideTimer);
1841            delete m.autoHideTimer;
1842        }
1843    }
1844
1845    // private
1846    function onBeforeShow(m){
1847        var pm = m.parentMenu;
1848        if(!pm && !m.allowOtherMenus){
1849            hideAll();
1850        }else if(pm && pm.activeChild && active != m){
1851            pm.activeChild.hide();
1852        }
1853    }
1854
1855    // private this should really trigger on mouseup..
1856    function onMouseDown(e){
1857         Roo.log("on Mouse Up");
1858         
1859         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860             Roo.log("MenuManager hideAll");
1861             hideAll();
1862             e.stopEvent();
1863         }
1864         
1865         
1866    }
1867
1868    // private
1869    function onBeforeCheck(mi, state){
1870        if(state){
1871            var g = groups[mi.group];
1872            for(var i = 0, l = g.length; i < l; i++){
1873                if(g[i] != mi){
1874                    g[i].setChecked(false);
1875                }
1876            }
1877        }
1878    }
1879
1880    return {
1881
1882        /**
1883         * Hides all menus that are currently visible
1884         */
1885        hideAll : function(){
1886             hideAll();  
1887        },
1888
1889        // private
1890        register : function(menu){
1891            if(!menus){
1892                init();
1893            }
1894            menus[menu.id] = menu;
1895            menu.on("beforehide", onBeforeHide);
1896            menu.on("hide", onHide);
1897            menu.on("beforeshow", onBeforeShow);
1898            menu.on("show", onShow);
1899            var g = menu.group;
1900            if(g && menu.events["checkchange"]){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menu);
1905                menu.on("checkchange", onCheck);
1906            }
1907        },
1908
1909         /**
1910          * Returns a {@link Roo.menu.Menu} object
1911          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912          * be used to generate and return a new Menu instance.
1913          */
1914        get : function(menu){
1915            if(typeof menu == "string"){ // menu id
1916                return menus[menu];
1917            }else if(menu.events){  // menu instance
1918                return menu;
1919            }
1920            /*else if(typeof menu.length == 'number'){ // array of menu items?
1921                return new Roo.bootstrap.Menu({items:menu});
1922            }else{ // otherwise, must be a config
1923                return new Roo.bootstrap.Menu(menu);
1924            }
1925            */
1926            return false;
1927        },
1928
1929        // private
1930        unregister : function(menu){
1931            delete menus[menu.id];
1932            menu.un("beforehide", onBeforeHide);
1933            menu.un("hide", onHide);
1934            menu.un("beforeshow", onBeforeShow);
1935            menu.un("show", onShow);
1936            var g = menu.group;
1937            if(g && menu.events["checkchange"]){
1938                groups[g].remove(menu);
1939                menu.un("checkchange", onCheck);
1940            }
1941        },
1942
1943        // private
1944        registerCheckable : function(menuItem){
1945            var g = menuItem.group;
1946            if(g){
1947                if(!groups[g]){
1948                    groups[g] = [];
1949                }
1950                groups[g].push(menuItem);
1951                menuItem.on("beforecheckchange", onBeforeCheck);
1952            }
1953        },
1954
1955        // private
1956        unregisterCheckable : function(menuItem){
1957            var g = menuItem.group;
1958            if(g){
1959                groups[g].remove(menuItem);
1960                menuItem.un("beforecheckchange", onBeforeCheck);
1961            }
1962        }
1963    };
1964 }();/*
1965  * - LGPL
1966  *
1967  * menu
1968  * 
1969  */
1970
1971 /**
1972  * @class Roo.bootstrap.Menu
1973  * @extends Roo.bootstrap.Component
1974  * Bootstrap Menu class - container for MenuItems
1975  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1977  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1978  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1979  * 
1980  * @constructor
1981  * Create a new Menu
1982  * @param {Object} config The config object
1983  */
1984
1985
1986 Roo.bootstrap.Menu = function(config){
1987     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988     if (this.registerMenu && this.type != 'treeview')  {
1989         Roo.bootstrap.MenuMgr.register(this);
1990     }
1991     this.addEvents({
1992         /**
1993          * @event beforeshow
1994          * Fires before this menu is displayed
1995          * @param {Roo.menu.Menu} this
1996          */
1997         beforeshow : true,
1998         /**
1999          * @event beforehide
2000          * Fires before this menu is hidden
2001          * @param {Roo.menu.Menu} this
2002          */
2003         beforehide : true,
2004         /**
2005          * @event show
2006          * Fires after this menu is displayed
2007          * @param {Roo.menu.Menu} this
2008          */
2009         show : true,
2010         /**
2011          * @event hide
2012          * Fires after this menu is hidden
2013          * @param {Roo.menu.Menu} this
2014          */
2015         hide : true,
2016         /**
2017          * @event click
2018          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021          * @param {Roo.EventObject} e
2022          */
2023         click : true,
2024         /**
2025          * @event mouseover
2026          * Fires when the mouse is hovering over this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseover : true,
2032         /**
2033          * @event mouseout
2034          * Fires when the mouse exits this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseout : true,
2040         /**
2041          * @event itemclick
2042          * Fires when a menu item contained in this menu is clicked
2043          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044          * @param {Roo.EventObject} e
2045          */
2046         itemclick: true
2047     });
2048     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2049 };
2050
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2052     
2053    /// html : false,
2054     //align : '',
2055     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2056     type: false,
2057     /**
2058      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2059      */
2060     registerMenu : true,
2061     
2062     menuItems :false, // stores the menu items..
2063     
2064     hidden:true,
2065         
2066     parentMenu : false,
2067     
2068     stopEvent : true,
2069     
2070     isLink : false,
2071     
2072     getChildContainer : function() {
2073         return this.el;  
2074     },
2075     
2076     getAutoCreate : function(){
2077          
2078         //if (['right'].indexOf(this.align)!==-1) {
2079         //    cfg.cn[1].cls += ' pull-right'
2080         //}
2081         
2082         
2083         var cfg = {
2084             tag : 'ul',
2085             cls : 'dropdown-menu' ,
2086             style : 'z-index:1000'
2087             
2088         };
2089         
2090         if (this.type === 'submenu') {
2091             cfg.cls = 'submenu active';
2092         }
2093         if (this.type === 'treeview') {
2094             cfg.cls = 'treeview-menu';
2095         }
2096         
2097         return cfg;
2098     },
2099     initEvents : function() {
2100         
2101        // Roo.log("ADD event");
2102        // Roo.log(this.triggerEl.dom);
2103         
2104         this.triggerEl.on('click', this.onTriggerClick, this);
2105         
2106         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2107         
2108         this.triggerEl.addClass('dropdown-toggle');
2109         
2110         if (Roo.isTouch) {
2111             this.el.on('touchstart'  , this.onTouch, this);
2112         }
2113         this.el.on('click' , this.onClick, this);
2114
2115         this.el.on("mouseover", this.onMouseOver, this);
2116         this.el.on("mouseout", this.onMouseOut, this);
2117         
2118     },
2119     
2120     findTargetItem : function(e)
2121     {
2122         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2123         if(!t){
2124             return false;
2125         }
2126         //Roo.log(t);         Roo.log(t.id);
2127         if(t && t.id){
2128             //Roo.log(this.menuitems);
2129             return this.menuitems.get(t.id);
2130             
2131             //return this.items.get(t.menuItemId);
2132         }
2133         
2134         return false;
2135     },
2136     
2137     onTouch : function(e) 
2138     {
2139         Roo.log("menu.onTouch");
2140         //e.stopEvent(); this make the user popdown broken
2141         this.onClick(e);
2142     },
2143     
2144     onClick : function(e)
2145     {
2146         Roo.log("menu.onClick");
2147         
2148         var t = this.findTargetItem(e);
2149         if(!t || t.isContainer){
2150             return;
2151         }
2152         Roo.log(e);
2153         /*
2154         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2155             if(t == this.activeItem && t.shouldDeactivate(e)){
2156                 this.activeItem.deactivate();
2157                 delete this.activeItem;
2158                 return;
2159             }
2160             if(t.canActivate){
2161                 this.setActiveItem(t, true);
2162             }
2163             return;
2164             
2165             
2166         }
2167         */
2168        
2169         Roo.log('pass click event');
2170         
2171         t.onClick(e);
2172         
2173         this.fireEvent("click", this, t, e);
2174         
2175         var _this = this;
2176         
2177         if(!t.href.length || t.href == '#'){
2178             (function() { _this.hide(); }).defer(100);
2179         }
2180         
2181     },
2182     
2183     onMouseOver : function(e){
2184         var t  = this.findTargetItem(e);
2185         //Roo.log(t);
2186         //if(t){
2187         //    if(t.canActivate && !t.disabled){
2188         //        this.setActiveItem(t, true);
2189         //    }
2190         //}
2191         
2192         this.fireEvent("mouseover", this, e, t);
2193     },
2194     isVisible : function(){
2195         return !this.hidden;
2196     },
2197      onMouseOut : function(e){
2198         var t  = this.findTargetItem(e);
2199         
2200         //if(t ){
2201         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2202         //        this.activeItem.deactivate();
2203         //        delete this.activeItem;
2204         //    }
2205         //}
2206         this.fireEvent("mouseout", this, e, t);
2207     },
2208     
2209     
2210     /**
2211      * Displays this menu relative to another element
2212      * @param {String/HTMLElement/Roo.Element} element The element to align to
2213      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214      * the element (defaults to this.defaultAlign)
2215      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2216      */
2217     show : function(el, pos, parentMenu){
2218         this.parentMenu = parentMenu;
2219         if(!this.el){
2220             this.render();
2221         }
2222         this.fireEvent("beforeshow", this);
2223         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2224     },
2225      /**
2226      * Displays this menu at a specific xy position
2227      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     showAt : function(xy, parentMenu, /* private: */_e){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         if(_e !== false){
2236             this.fireEvent("beforeshow", this);
2237             //xy = this.el.adjustForConstraints(xy);
2238         }
2239         
2240         //this.el.show();
2241         this.hideMenuItems();
2242         this.hidden = false;
2243         this.triggerEl.addClass('open');
2244         
2245         // reassign x when hitting right
2246         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2248         }
2249         
2250         // reassign y when hitting bottom
2251         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2253         }
2254         
2255         // but the list may align on trigger left or trigger top... should it be a properity?
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  * @cfg {Number} max_width set the max width of modal
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628     
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637     
2638     max_width: 0,
2639     
2640     max_height: 0,
2641     
2642     fit_content: false,
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         
2681         //this.el.addClass("x-dlg-modal");
2682
2683         if (this.buttons.length) {
2684             Roo.each(this.buttons, function(bb) {
2685                 var b = Roo.apply({}, bb);
2686                 b.xns = b.xns || Roo.bootstrap;
2687                 b.xtype = b.xtype || 'Button';
2688                 if (typeof(b.listeners) == 'undefined') {
2689                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2690                 }
2691
2692                 var btn = Roo.factory(b);
2693
2694                 btn.render(this.el.select('.modal-footer div').first());
2695
2696             },this);
2697         }
2698         // render the children.
2699         var nitems = [];
2700
2701         if(typeof(this.items) != 'undefined'){
2702             var items = this.items;
2703             delete this.items;
2704
2705             for(var i =0;i < items.length;i++) {
2706                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2707             }
2708         }
2709
2710         this.items = nitems;
2711
2712         // where are these used - they used to be body/close/footer
2713
2714
2715         this.initEvents();
2716         //this.el.addClass([this.fieldClass, this.cls]);
2717
2718     },
2719
2720     getAutoCreate : function()
2721     {
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(
2819             Roo.lib.Dom.getViewWidth(true),
2820             Roo.lib.Dom.getViewHeight(true)
2821         );
2822         
2823         if (this.fitwindow) {
2824             this.setSize(
2825                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2827             );
2828             return;
2829         }
2830         
2831         if(this.max_width !== 0) {
2832             
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             if(this.height) {
2836                 this.setSize(w, this.height);
2837                 return;
2838             }
2839             
2840             if(this.max_height) {
2841                 this.setSize(w,Math.min(
2842                     this.max_height,
2843                     Roo.lib.Dom.getViewportHeight(true) - 60
2844                 ));
2845                 
2846                 return;
2847             }
2848             
2849             if(!this.fit_content) {
2850                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2851                 return;
2852             }
2853             
2854             this.setSize(w, Math.min(
2855                 60 +
2856                 this.headerEl.getHeight() + 
2857                 this.footerEl.getHeight() + 
2858                 this.getChildHeight(this.bodyEl.dom.childNodes),
2859                 Roo.lib.Dom.getViewportHeight(true) - 60)
2860             );
2861         }
2862         
2863     },
2864
2865     setSize : function(w,h)
2866     {
2867         if (!w && !h) {
2868             return;
2869         }
2870         
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h - this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     },
3024     
3025     getChildHeight : function(child_nodes)
3026     {
3027         if(
3028             !child_nodes ||
3029             child_nodes.length == 0
3030         ) {
3031             return;
3032         }
3033         
3034         var child_height = 0;
3035         
3036         for(var i = 0; i < child_nodes.length; i++) {
3037             
3038             /*
3039             * for modal with tabs...
3040             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3041                 
3042                 var layout_childs = child_nodes[i].childNodes;
3043                 
3044                 for(var j = 0; j < layout_childs.length; j++) {
3045                     
3046                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3047                         
3048                         var layout_body_childs = layout_childs[j].childNodes;
3049                         
3050                         for(var k = 0; k < layout_body_childs.length; k++) {
3051                             
3052                             if(layout_body_childs[k].classList.contains('navbar')) {
3053                                 child_height += layout_body_childs[k].offsetHeight;
3054                                 continue;
3055                             }
3056                             
3057                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3058                                 
3059                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3060                                 
3061                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3062                                     
3063                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3065                                         continue;
3066                                     }
3067                                     
3068                                 }
3069                                 
3070                             }
3071                             
3072                         }
3073                     }
3074                 }
3075                 continue;
3076             }
3077             */
3078             
3079             child_height += child_nodes[i].offsetHeight;
3080             // Roo.log(child_nodes[i].offsetHeight);
3081         }
3082         
3083         return child_height;
3084     }
3085
3086 });
3087
3088
3089 Roo.apply(Roo.bootstrap.Modal,  {
3090     /**
3091          * Button config that displays a single OK button
3092          * @type Object
3093          */
3094         OK :  [{
3095             name : 'ok',
3096             weight : 'primary',
3097             html : 'OK'
3098         }],
3099         /**
3100          * Button config that displays Yes and No buttons
3101          * @type Object
3102          */
3103         YESNO : [
3104             {
3105                 name  : 'no',
3106                 html : 'No'
3107             },
3108             {
3109                 name  :'yes',
3110                 weight : 'primary',
3111                 html : 'Yes'
3112             }
3113         ],
3114
3115         /**
3116          * Button config that displays OK and Cancel buttons
3117          * @type Object
3118          */
3119         OKCANCEL : [
3120             {
3121                name : 'cancel',
3122                 html : 'Cancel'
3123             },
3124             {
3125                 name : 'ok',
3126                 weight : 'primary',
3127                 html : 'OK'
3128             }
3129         ],
3130         /**
3131          * Button config that displays Yes, No and Cancel buttons
3132          * @type Object
3133          */
3134         YESNOCANCEL : [
3135             {
3136                 name : 'yes',
3137                 weight : 'primary',
3138                 html : 'Yes'
3139             },
3140             {
3141                 name : 'no',
3142                 html : 'No'
3143             },
3144             {
3145                 name : 'cancel',
3146                 html : 'Cancel'
3147             }
3148         ],
3149         
3150         zIndex : 10001
3151 });
3152 /*
3153  * - LGPL
3154  *
3155  * messagebox - can be used as a replace
3156  * 
3157  */
3158 /**
3159  * @class Roo.MessageBox
3160  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3161  * Example usage:
3162  *<pre><code>
3163 // Basic alert:
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3165
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3168     if (btn == 'ok'){
3169         // process text value...
3170     }
3171 });
3172
3173 // Show a dialog using config options:
3174 Roo.Msg.show({
3175    title:'Save Changes?',
3176    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177    buttons: Roo.Msg.YESNOCANCEL,
3178    fn: processResult,
3179    animEl: 'elId'
3180 });
3181 </code></pre>
3182  * @singleton
3183  */
3184 Roo.bootstrap.MessageBox = function(){
3185     var dlg, opt, mask, waitTimer;
3186     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187     var buttons, activeTextEl, bwidth;
3188
3189     
3190     // private
3191     var handleButton = function(button){
3192         dlg.hide();
3193         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3194     };
3195
3196     // private
3197     var handleHide = function(){
3198         if(opt && opt.cls){
3199             dlg.el.removeClass(opt.cls);
3200         }
3201         //if(waitTimer){
3202         //    Roo.TaskMgr.stop(waitTimer);
3203         //    waitTimer = null;
3204         //}
3205     };
3206
3207     // private
3208     var updateButtons = function(b){
3209         var width = 0;
3210         if(!b){
3211             buttons["ok"].hide();
3212             buttons["cancel"].hide();
3213             buttons["yes"].hide();
3214             buttons["no"].hide();
3215             //dlg.footer.dom.style.display = 'none';
3216             return width;
3217         }
3218         dlg.footerEl.dom.style.display = '';
3219         for(var k in buttons){
3220             if(typeof buttons[k] != "function"){
3221                 if(b[k]){
3222                     buttons[k].show();
3223                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224                     width += buttons[k].el.getWidth()+15;
3225                 }else{
3226                     buttons[k].hide();
3227                 }
3228             }
3229         }
3230         return width;
3231     };
3232
3233     // private
3234     var handleEsc = function(d, k, e){
3235         if(opt && opt.closable !== false){
3236             dlg.hide();
3237         }
3238         if(e){
3239             e.stopEvent();
3240         }
3241     };
3242
3243     return {
3244         /**
3245          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246          * @return {Roo.BasicDialog} The BasicDialog element
3247          */
3248         getDialog : function(){
3249            if(!dlg){
3250                 dlg = new Roo.bootstrap.Modal( {
3251                     //draggable: true,
3252                     //resizable:false,
3253                     //constraintoviewport:false,
3254                     //fixedcenter:true,
3255                     //collapsible : false,
3256                     //shim:true,
3257                     //modal: true,
3258                 //    width: 'auto',
3259                   //  height:100,
3260                     //buttonAlign:"center",
3261                     closeClick : function(){
3262                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3263                             handleButton("no");
3264                         }else{
3265                             handleButton("cancel");
3266                         }
3267                     }
3268                 });
3269                 dlg.render();
3270                 dlg.on("hide", handleHide);
3271                 mask = dlg.mask;
3272                 //dlg.addKeyListener(27, handleEsc);
3273                 buttons = {};
3274                 this.buttons = buttons;
3275                 var bt = this.buttonText;
3276                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3280                 //Roo.log(buttons);
3281                 bodyEl = dlg.bodyEl.createChild({
3282
3283                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284                         '<textarea class="roo-mb-textarea"></textarea>' +
3285                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3286                 });
3287                 msgEl = bodyEl.dom.firstChild;
3288                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289                 textboxEl.enableDisplayMode();
3290                 textboxEl.addKeyListener([10,13], function(){
3291                     if(dlg.isVisible() && opt && opt.buttons){
3292                         if(opt.buttons.ok){
3293                             handleButton("ok");
3294                         }else if(opt.buttons.yes){
3295                             handleButton("yes");
3296                         }
3297                     }
3298                 });
3299                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300                 textareaEl.enableDisplayMode();
3301                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302                 progressEl.enableDisplayMode();
3303                 
3304                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305                 var pf = progressEl.dom.firstChild;
3306                 if (pf) {
3307                     pp = Roo.get(pf.firstChild);
3308                     pp.setHeight(pf.offsetHeight);
3309                 }
3310                 
3311             }
3312             return dlg;
3313         },
3314
3315         /**
3316          * Updates the message box body text
3317          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318          * the XHTML-compliant non-breaking space character '&amp;#160;')
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         updateText : function(text)
3322         {
3323             if(!dlg.isVisible() && !opt.width){
3324                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3326             }
3327             msgEl.innerHTML = text || '&#160;';
3328       
3329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3331             var w = Math.max(
3332                     Math.min(opt.width || cw , this.maxWidth), 
3333                     Math.max(opt.minWidth || this.minWidth, bwidth)
3334             );
3335             if(opt.prompt){
3336                 activeTextEl.setWidth(w);
3337             }
3338             if(dlg.isVisible()){
3339                 dlg.fixedcenter = false;
3340             }
3341             // to big, make it scroll. = But as usual stupid IE does not support
3342             // !important..
3343             
3344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3347             } else {
3348                 bodyEl.dom.style.height = '';
3349                 bodyEl.dom.style.overflowY = '';
3350             }
3351             if (cw > w) {
3352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3353             } else {
3354                 bodyEl.dom.style.overflowX = '';
3355             }
3356             
3357             dlg.setContentSize(w, bodyEl.getHeight());
3358             if(dlg.isVisible()){
3359                 dlg.fixedcenter = true;
3360             }
3361             return this;
3362         },
3363
3364         /**
3365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         updateProgress : function(value, text){
3372             if(text){
3373                 this.updateText(text);
3374             }
3375             
3376             if (pp) { // weird bug on my firefox - for some reason this is not defined
3377                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3379             }
3380             return this;
3381         },        
3382
3383         /**
3384          * Returns true if the message box is currently displayed
3385          * @return {Boolean} True if the message box is visible, else false
3386          */
3387         isVisible : function(){
3388             return dlg && dlg.isVisible();  
3389         },
3390
3391         /**
3392          * Hides the message box if it is displayed
3393          */
3394         hide : function(){
3395             if(this.isVisible()){
3396                 dlg.hide();
3397             }  
3398         },
3399
3400         /**
3401          * Displays a new message box, or reinitializes an existing message box, based on the config options
3402          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403          * The following config object properties are supported:
3404          * <pre>
3405 Property    Type             Description
3406 ----------  ---------------  ------------------------------------------------------------------------------------
3407 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3408                                    closes (defaults to undefined)
3409 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3412                                    progress and wait dialogs will ignore this property and always hide the
3413                                    close button as they can only be closed programmatically.
3414 cls               String           A custom CSS class to apply to the message box element
3415 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3416                                    displayed (defaults to 75)
3417 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3418                                    function will be btn (the name of the button that was clicked, if applicable,
3419                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3420                                    Progress and wait dialogs will ignore this option since they do not respond to
3421                                    user actions and can only be closed programmatically, so any required function
3422                                    should be called by the same code after it closes the dialog.
3423 icon              String           A CSS class that provides a background image to be used as an icon for
3424                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3426 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3427 modal             Boolean          False to allow user interaction with the page while the message box is
3428                                    displayed (defaults to true)
3429 msg               String           A string that will replace the existing message box body text (defaults
3430                                    to the XHTML-compliant non-breaking space character '&#160;')
3431 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3432 progress          Boolean          True to display a progress bar (defaults to false)
3433 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3436 title             String           The title text
3437 value             String           The string value to set into the active textbox element if displayed
3438 wait              Boolean          True to display a progress bar (defaults to false)
3439 width             Number           The width of the dialog in pixels
3440 </pre>
3441          *
3442          * Example usage:
3443          * <pre><code>
3444 Roo.Msg.show({
3445    title: 'Address',
3446    msg: 'Please enter your address:',
3447    width: 300,
3448    buttons: Roo.MessageBox.OKCANCEL,
3449    multiline: true,
3450    fn: saveAddress,
3451    animEl: 'addAddressBtn'
3452 });
3453 </code></pre>
3454          * @param {Object} config Configuration options
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         show : function(options)
3458         {
3459             
3460             // this causes nightmares if you show one dialog after another
3461             // especially on callbacks..
3462              
3463             if(this.isVisible()){
3464                 
3465                 this.hide();
3466                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3468                 Roo.log("New Dialog Message:" +  options.msg )
3469                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3471                 
3472             }
3473             var d = this.getDialog();
3474             opt = options;
3475             d.setTitle(opt.title || "&#160;");
3476             d.closeEl.setDisplayed(opt.closable !== false);
3477             activeTextEl = textboxEl;
3478             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3479             if(opt.prompt){
3480                 if(opt.multiline){
3481                     textboxEl.hide();
3482                     textareaEl.show();
3483                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3484                         opt.multiline : this.defaultTextHeight);
3485                     activeTextEl = textareaEl;
3486                 }else{
3487                     textboxEl.show();
3488                     textareaEl.hide();
3489                 }
3490             }else{
3491                 textboxEl.hide();
3492                 textareaEl.hide();
3493             }
3494             progressEl.setDisplayed(opt.progress === true);
3495             this.updateProgress(0);
3496             activeTextEl.dom.value = opt.value || "";
3497             if(opt.prompt){
3498                 dlg.setDefaultButton(activeTextEl);
3499             }else{
3500                 var bs = opt.buttons;
3501                 var db = null;
3502                 if(bs && bs.ok){
3503                     db = buttons["ok"];
3504                 }else if(bs && bs.yes){
3505                     db = buttons["yes"];
3506                 }
3507                 dlg.setDefaultButton(db);
3508             }
3509             bwidth = updateButtons(opt.buttons);
3510             this.updateText(opt.msg);
3511             if(opt.cls){
3512                 d.el.addClass(opt.cls);
3513             }
3514             d.proxyDrag = opt.proxyDrag === true;
3515             d.modal = opt.modal !== false;
3516             d.mask = opt.modal !== false ? mask : false;
3517             if(!d.isVisible()){
3518                 // force it to the end of the z-index stack so it gets a cursor in FF
3519                 document.body.appendChild(dlg.el.dom);
3520                 d.animateTarget = null;
3521                 d.show(options.animEl);
3522             }
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3528          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529          * and closing the message box when the process is complete.
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         progress : function(title, msg){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: false,
3539                 progress:true,
3540                 closable:false,
3541                 minWidth: this.minProgressWidth,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549          * If a callback function is passed it will be called after the user clicks the button, and the
3550          * id of the button that was clicked will be passed as the only parameter to the callback
3551          * (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         alert : function(title, msg, fn, scope)
3559         {
3560             this.show({
3561                 title : title,
3562                 msg : msg,
3563                 buttons: this.OK,
3564                 fn: fn,
3565                 closable : false,
3566                 scope : scope,
3567                 modal : true
3568             });
3569             return this;
3570         },
3571
3572         /**
3573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575          * You are responsible for closing the message box when the process is complete.
3576          * @param {String} msg The message box body text
3577          * @param {String} title (optional) The title bar text
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         wait : function(msg, title){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: false,
3585                 closable:false,
3586                 progress:true,
3587                 modal:true,
3588                 width:300,
3589                 wait:true
3590             });
3591             waitTimer = Roo.TaskMgr.start({
3592                 run: function(i){
3593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594                 },
3595                 interval: 1000
3596             });
3597             return this;
3598         },
3599
3600         /**
3601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604          * @param {String} title The title bar text
3605          * @param {String} msg The message box body text
3606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607          * @param {Object} scope (optional) The scope of the callback function
3608          * @return {Roo.MessageBox} This message box
3609          */
3610         confirm : function(title, msg, fn, scope){
3611             this.show({
3612                 title : title,
3613                 msg : msg,
3614                 buttons: this.YESNO,
3615                 fn: fn,
3616                 scope : scope,
3617                 modal : true
3618             });
3619             return this;
3620         },
3621
3622         /**
3623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626          * (could also be the top-right close button) and the text that was entered will be passed as the two
3627          * parameters to the callback.
3628          * @param {String} title The title bar text
3629          * @param {String} msg The message box body text
3630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631          * @param {Object} scope (optional) The scope of the callback function
3632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634          * @return {Roo.MessageBox} This message box
3635          */
3636         prompt : function(title, msg, fn, scope, multiline){
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OKCANCEL,
3641                 fn: fn,
3642                 minWidth:250,
3643                 scope : scope,
3644                 prompt:true,
3645                 multiline: multiline,
3646                 modal : true
3647             });
3648             return this;
3649         },
3650
3651         /**
3652          * Button config that displays a single OK button
3653          * @type Object
3654          */
3655         OK : {ok:true},
3656         /**
3657          * Button config that displays Yes and No buttons
3658          * @type Object
3659          */
3660         YESNO : {yes:true, no:true},
3661         /**
3662          * Button config that displays OK and Cancel buttons
3663          * @type Object
3664          */
3665         OKCANCEL : {ok:true, cancel:true},
3666         /**
3667          * Button config that displays Yes, No and Cancel buttons
3668          * @type Object
3669          */
3670         YESNOCANCEL : {yes:true, no:true, cancel:true},
3671
3672         /**
3673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3674          * @type Number
3675          */
3676         defaultTextHeight : 75,
3677         /**
3678          * The maximum width in pixels of the message box (defaults to 600)
3679          * @type Number
3680          */
3681         maxWidth : 600,
3682         /**
3683          * The minimum width in pixels of the message box (defaults to 100)
3684          * @type Number
3685          */
3686         minWidth : 100,
3687         /**
3688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3690          * @type Number
3691          */
3692         minProgressWidth : 250,
3693         /**
3694          * An object containing the default button text strings that can be overriden for localized language support.
3695          * Supported properties are: ok, cancel, yes and no.
3696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3697          * @type Object
3698          */
3699         buttonText : {
3700             ok : "OK",
3701             cancel : "Cancel",
3702             yes : "Yes",
3703             no : "No"
3704         }
3705     };
3706 }();
3707
3708 /**
3709  * Shorthand for {@link Roo.MessageBox}
3710  */
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3713 /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.Navbar
3722  * @extends Roo.bootstrap.Component
3723  * Bootstrap Navbar class
3724
3725  * @constructor
3726  * Create a new Navbar
3727  * @param {Object} config The config object
3728  */
3729
3730
3731 Roo.bootstrap.Navbar = function(config){
3732     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3733     this.addEvents({
3734         // raw events
3735         /**
3736          * @event beforetoggle
3737          * Fire before toggle the menu
3738          * @param {Roo.EventObject} e
3739          */
3740         "beforetoggle" : true
3741     });
3742 };
3743
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3745     
3746     
3747    
3748     // private
3749     navItems : false,
3750     loadMask : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3757         
3758     },
3759     
3760     initEvents :function ()
3761     {
3762         //Roo.log(this.el.select('.navbar-toggle',true));
3763         this.el.select('.navbar-toggle',true).on('click', function() {
3764             if(this.fireEvent('beforetoggle', this) !== false){
3765                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3766             }
3767             
3768         }, this);
3769         
3770         var mark = {
3771             tag: "div",
3772             cls:"x-dlg-mask"
3773         };
3774         
3775         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3776         
3777         var size = this.el.getSize();
3778         this.maskEl.setSize(size.width, size.height);
3779         this.maskEl.enableDisplayMode("block");
3780         this.maskEl.hide();
3781         
3782         if(this.loadMask){
3783             this.maskEl.show();
3784         }
3785     },
3786     
3787     
3788     getChildContainer : function()
3789     {
3790         if (this.el.select('.collapse').getCount()) {
3791             return this.el.select('.collapse',true).first();
3792         }
3793         
3794         return this.el;
3795     },
3796     
3797     mask : function()
3798     {
3799         this.maskEl.show();
3800     },
3801     
3802     unmask : function()
3803     {
3804         this.maskEl.hide();
3805     } 
3806     
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  /*
3817  * - LGPL
3818  *
3819  * navbar
3820  * 
3821  */
3822
3823 /**
3824  * @class Roo.bootstrap.NavSimplebar
3825  * @extends Roo.bootstrap.Navbar
3826  * Bootstrap Sidebar class
3827  *
3828  * @cfg {Boolean} inverse is inverted color
3829  * 
3830  * @cfg {String} type (nav | pills | tabs)
3831  * @cfg {Boolean} arrangement stacked | justified
3832  * @cfg {String} align (left | right) alignment
3833  * 
3834  * @cfg {Boolean} main (true|false) main nav bar? default false
3835  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3836  * 
3837  * @cfg {String} tag (header|footer|nav|div) default is nav 
3838
3839  * 
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new Sidebar
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.NavSimplebar = function(config){
3849     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3853     
3854     inverse: false,
3855     
3856     type: false,
3857     arrangement: '',
3858     align : false,
3859     
3860     
3861     
3862     main : false,
3863     
3864     
3865     tag : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         var cfg = {
3872             tag : this.tag || 'div',
3873             cls : 'navbar'
3874         };
3875           
3876         
3877         cfg.cn = [
3878             {
3879                 cls: 'nav',
3880                 tag : 'ul'
3881             }
3882         ];
3883         
3884          
3885         this.type = this.type || 'nav';
3886         if (['tabs','pills'].indexOf(this.type)!==-1) {
3887             cfg.cn[0].cls += ' nav-' + this.type
3888         
3889         
3890         } else {
3891             if (this.type!=='nav') {
3892                 Roo.log('nav type must be nav/tabs/pills')
3893             }
3894             cfg.cn[0].cls += ' navbar-nav'
3895         }
3896         
3897         
3898         
3899         
3900         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.arrangement;
3902         }
3903         
3904         
3905         if (this.align === 'right') {
3906             cfg.cn[0].cls += ' navbar-right';
3907         }
3908         
3909         if (this.inverse) {
3910             cfg.cls += ' navbar-inverse';
3911             
3912         }
3913         
3914         
3915         return cfg;
3916     
3917         
3918     }
3919     
3920     
3921     
3922 });
3923
3924
3925
3926  
3927
3928  
3929        /*
3930  * - LGPL
3931  *
3932  * navbar
3933  * navbar-fixed-top
3934  * navbar-expand-md  fixed-top 
3935  */
3936
3937 /**
3938  * @class Roo.bootstrap.NavHeaderbar
3939  * @extends Roo.bootstrap.NavSimplebar
3940  * Bootstrap Sidebar class
3941  *
3942  * @cfg {String} brand what is brand
3943  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944  * @cfg {String} brand_href href of the brand
3945  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3946  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3949  * 
3950  * @constructor
3951  * Create a new Sidebar
3952  * @param {Object} config The config object
3953  */
3954
3955
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3958       
3959 };
3960
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3962     
3963     position: '',
3964     brand: '',
3965     brand_href: false,
3966     srButton : true,
3967     autohide : false,
3968     desktopCenter : false,
3969    
3970     
3971     getAutoCreate : function(){
3972         
3973         var   cfg = {
3974             tag: this.nav || 'nav',
3975             cls: 'navbar',
3976             role: 'navigation',
3977             cn: []
3978         };
3979         
3980         var cn = cfg.cn;
3981         if (this.desktopCenter) {
3982             cn.push({cls : 'container', cn : []});
3983             cn = cn[0].cn;
3984         }
3985         
3986         if(this.srButton){
3987             cn.push({
3988                 tag: 'div',
3989                 cls: 'navbar-header',
3990                 cn: [
3991                     {
3992                         tag: 'button',
3993                         type: 'button',
3994                         cls: 'navbar-toggle',
3995                         'data-toggle': 'collapse',
3996                         cn: [
3997                             {
3998                                 tag: 'span',
3999                                 cls: 'sr-only',
4000                                 html: 'Toggle navigation'
4001                             },
4002                             {
4003                                 tag: 'span',
4004                                 cls: 'icon-bar'
4005                             },
4006                             {
4007                                 tag: 'span',
4008                                 cls: 'icon-bar'
4009                             },
4010                             {
4011                                 tag: 'span',
4012                                 cls: 'icon-bar'
4013                             }
4014                         ]
4015                     }
4016                 ]
4017             });
4018         }
4019         
4020         cn.push({
4021             tag: 'div',
4022             cls: 'collapse navbar-collapse',
4023             cn : []
4024         });
4025         
4026         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4027         
4028         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4030             
4031             // tag can override this..
4032             
4033             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4034         }
4035         
4036         if (this.brand !== '') {
4037             cn[0].cn.push({
4038                 tag: 'a',
4039                 href: this.brand_href ? this.brand_href : '#',
4040                 cls: 'navbar-brand',
4041                 cn: [
4042                 this.brand
4043                 ]
4044             });
4045         }
4046         
4047         if(this.main){
4048             cfg.cls += ' main-nav';
4049         }
4050         
4051         
4052         return cfg;
4053
4054         
4055     },
4056     getHeaderChildContainer : function()
4057     {
4058         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059             return this.el.select('.navbar-header',true).first();
4060         }
4061         
4062         return this.getChildContainer();
4063     },
4064     
4065     
4066     initEvents : function()
4067     {
4068         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069         
4070         if (this.autohide) {
4071             
4072             var prevScroll = 0;
4073             var ft = this.el;
4074             
4075             Roo.get(document).on('scroll',function(e) {
4076                 var ns = Roo.get(document).getScroll().top;
4077                 var os = prevScroll;
4078                 prevScroll = ns;
4079                 
4080                 if(ns > os){
4081                     ft.removeClass('slideDown');
4082                     ft.addClass('slideUp');
4083                     return;
4084                 }
4085                 ft.removeClass('slideUp');
4086                 ft.addClass('slideDown');
4087                  
4088               
4089           },this);
4090         }
4091     }    
4092     
4093 });
4094
4095
4096
4097  
4098
4099  /*
4100  * - LGPL
4101  *
4102  * navbar
4103  * 
4104  */
4105
4106 /**
4107  * @class Roo.bootstrap.NavSidebar
4108  * @extends Roo.bootstrap.Navbar
4109  * Bootstrap Sidebar class
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavSidebar = function(config){
4118     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4119 };
4120
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4122     
4123     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124     
4125     getAutoCreate : function(){
4126         
4127         
4128         return  {
4129             tag: 'div',
4130             cls: 'sidebar sidebar-nav'
4131         };
4132     
4133         
4134     }
4135     
4136     
4137     
4138 });
4139
4140
4141
4142  
4143
4144  /*
4145  * - LGPL
4146  *
4147  * nav group
4148  * 
4149  */
4150
4151 /**
4152  * @class Roo.bootstrap.NavGroup
4153  * @extends Roo.bootstrap.Component
4154  * Bootstrap NavGroup class
4155  * @cfg {String} align (left|right)
4156  * @cfg {Boolean} inverse
4157  * @cfg {String} type (nav|pills|tab) default nav
4158  * @cfg {String} navId - reference Id for navbar.
4159
4160  * 
4161  * @constructor
4162  * Create a new nav group
4163  * @param {Object} config The config object
4164  */
4165
4166 Roo.bootstrap.NavGroup = function(config){
4167     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4168     this.navItems = [];
4169    
4170     Roo.bootstrap.NavGroup.register(this);
4171      this.addEvents({
4172         /**
4173              * @event changed
4174              * Fires when the active item changes
4175              * @param {Roo.bootstrap.NavGroup} this
4176              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4178          */
4179         'changed': true
4180      });
4181     
4182 };
4183
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4185     
4186     align: '',
4187     inverse: false,
4188     form: false,
4189     type: 'nav',
4190     navId : '',
4191     // private
4192     
4193     navItems : false, 
4194     
4195     getAutoCreate : function()
4196     {
4197         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4198         
4199         cfg = {
4200             tag : 'ul',
4201             cls: 'nav' 
4202         };
4203         
4204         if (['tabs','pills'].indexOf(this.type)!==-1) {
4205             cfg.cls += ' nav-' + this.type
4206         } else {
4207             if (this.type!=='nav') {
4208                 Roo.log('nav type must be nav/tabs/pills')
4209             }
4210             cfg.cls += ' navbar-nav'
4211         }
4212         
4213         if (this.parent() && this.parent().sidebar) {
4214             cfg = {
4215                 tag: 'ul',
4216                 cls: 'dashboard-menu sidebar-menu'
4217             };
4218             
4219             return cfg;
4220         }
4221         
4222         if (this.form === true) {
4223             cfg = {
4224                 tag: 'form',
4225                 cls: 'navbar-form'
4226             };
4227             
4228             if (this.align === 'right') {
4229                 cfg.cls += ' navbar-right';
4230             } else {
4231                 cfg.cls += ' navbar-left';
4232             }
4233         }
4234         
4235         if (this.align === 'right') {
4236             cfg.cls += ' navbar-right';
4237         }
4238         
4239         if (this.inverse) {
4240             cfg.cls += ' navbar-inverse';
4241             
4242         }
4243         
4244         
4245         return cfg;
4246     },
4247     /**
4248     * sets the active Navigation item
4249     * @param {Roo.bootstrap.NavItem} the new current navitem
4250     */
4251     setActiveItem : function(item)
4252     {
4253         var prev = false;
4254         Roo.each(this.navItems, function(v){
4255             if (v == item) {
4256                 return ;
4257             }
4258             if (v.isActive()) {
4259                 v.setActive(false, true);
4260                 prev = v;
4261                 
4262             }
4263             
4264         });
4265
4266         item.setActive(true, true);
4267         this.fireEvent('changed', this, item, prev);
4268         
4269         
4270     },
4271     /**
4272     * gets the active Navigation item
4273     * @return {Roo.bootstrap.NavItem} the current navitem
4274     */
4275     getActive : function()
4276     {
4277         
4278         var prev = false;
4279         Roo.each(this.navItems, function(v){
4280             
4281             if (v.isActive()) {
4282                 prev = v;
4283                 
4284             }
4285             
4286         });
4287         return prev;
4288     },
4289     
4290     indexOfNav : function()
4291     {
4292         
4293         var prev = false;
4294         Roo.each(this.navItems, function(v,i){
4295             
4296             if (v.isActive()) {
4297                 prev = i;
4298                 
4299             }
4300             
4301         });
4302         return prev;
4303     },
4304     /**
4305     * adds a Navigation item
4306     * @param {Roo.bootstrap.NavItem} the navitem to add
4307     */
4308     addItem : function(cfg)
4309     {
4310         var cn = new Roo.bootstrap.NavItem(cfg);
4311         this.register(cn);
4312         cn.parentId = this.id;
4313         cn.onRender(this.el, null);
4314         return cn;
4315     },
4316     /**
4317     * register a Navigation item
4318     * @param {Roo.bootstrap.NavItem} the navitem to add
4319     */
4320     register : function(item)
4321     {
4322         this.navItems.push( item);
4323         item.navId = this.navId;
4324     
4325     },
4326     
4327     /**
4328     * clear all the Navigation item
4329     */
4330    
4331     clearAll : function()
4332     {
4333         this.navItems = [];
4334         this.el.dom.innerHTML = '';
4335     },
4336     
4337     getNavItem: function(tabId)
4338     {
4339         var ret = false;
4340         Roo.each(this.navItems, function(e) {
4341             if (e.tabId == tabId) {
4342                ret =  e;
4343                return false;
4344             }
4345             return true;
4346             
4347         });
4348         return ret;
4349     },
4350     
4351     setActiveNext : function()
4352     {
4353         var i = this.indexOfNav(this.getActive());
4354         if (i > this.navItems.length) {
4355             return;
4356         }
4357         this.setActiveItem(this.navItems[i+1]);
4358     },
4359     setActivePrev : function()
4360     {
4361         var i = this.indexOfNav(this.getActive());
4362         if (i  < 1) {
4363             return;
4364         }
4365         this.setActiveItem(this.navItems[i-1]);
4366     },
4367     clearWasActive : function(except) {
4368         Roo.each(this.navItems, function(e) {
4369             if (e.tabId != except.tabId && e.was_active) {
4370                e.was_active = false;
4371                return false;
4372             }
4373             return true;
4374             
4375         });
4376     },
4377     getWasActive : function ()
4378     {
4379         var r = false;
4380         Roo.each(this.navItems, function(e) {
4381             if (e.was_active) {
4382                r = e;
4383                return false;
4384             }
4385             return true;
4386             
4387         });
4388         return r;
4389     }
4390     
4391     
4392 });
4393
4394  
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4396     
4397     groups: {},
4398      /**
4399     * register a Navigation Group
4400     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401     */
4402     register : function(navgrp)
4403     {
4404         this.groups[navgrp.navId] = navgrp;
4405         
4406     },
4407     /**
4408     * fetch a Navigation Group based on the navigation ID
4409     * @param {string} the navgroup to add
4410     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4411     */
4412     get: function(navId) {
4413         if (typeof(this.groups[navId]) == 'undefined') {
4414             return false;
4415             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416         }
4417         return this.groups[navId] ;
4418     }
4419     
4420     
4421     
4422 });
4423
4424  /*
4425  * - LGPL
4426  *
4427  * row
4428  * 
4429  */
4430
4431 /**
4432  * @class Roo.bootstrap.NavItem
4433  * @extends Roo.bootstrap.Component
4434  * Bootstrap Navbar.NavItem class
4435  * @cfg {String} href  link to
4436  * @cfg {String} html content of button
4437  * @cfg {String} badge text inside badge
4438  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439  * @cfg {String} glyphicon name of glyphicon
4440  * @cfg {String} icon name of font awesome icon
4441  * @cfg {Boolean} active Is item active
4442  * @cfg {Boolean} disabled Is item disabled
4443  
4444  * @cfg {Boolean} preventDefault (true | false) default false
4445  * @cfg {String} tabId the tab that this item activates.
4446  * @cfg {String} tagtype (a|span) render as a href or span?
4447  * @cfg {Boolean} animateRef (true|false) link to element default false  
4448   
4449  * @constructor
4450  * Create a new Navbar Item
4451  * @param {Object} config The config object
4452  */
4453 Roo.bootstrap.NavItem = function(config){
4454     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * The raw click event for the entire grid.
4460          * @param {Roo.EventObject} e
4461          */
4462         "click" : true,
4463          /**
4464             * @event changed
4465             * Fires when the active item active state changes
4466             * @param {Roo.bootstrap.NavItem} this
4467             * @param {boolean} state the new state
4468              
4469          */
4470         'changed': true,
4471         /**
4472             * @event scrollto
4473             * Fires when scroll to element
4474             * @param {Roo.bootstrap.NavItem} this
4475             * @param {Object} options
4476             * @param {Roo.EventObject} e
4477              
4478          */
4479         'scrollto': true
4480     });
4481    
4482 };
4483
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4485     
4486     href: false,
4487     html: '',
4488     badge: '',
4489     icon: false,
4490     glyphicon: false,
4491     active: false,
4492     preventDefault : false,
4493     tabId : false,
4494     tagtype : 'a',
4495     disabled : false,
4496     animateRef : false,
4497     was_active : false,
4498     
4499     getAutoCreate : function(){
4500          
4501         var cfg = {
4502             tag: 'li',
4503             cls: 'nav-item'
4504             
4505         };
4506         
4507         if (this.active) {
4508             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509         }
4510         if (this.disabled) {
4511             cfg.cls += ' disabled';
4512         }
4513         
4514         if (this.href || this.html || this.glyphicon || this.icon) {
4515             cfg.cn = [
4516                 {
4517                     tag: this.tagtype,
4518                     href : this.href || "#",
4519                     html: this.html || ''
4520                 }
4521             ];
4522             
4523             if (this.icon) {
4524                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4525             }
4526
4527             if(this.glyphicon) {
4528                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4529             }
4530             
4531             if (this.menu) {
4532                 
4533                 cfg.cn[0].html += " <span class='caret'></span>";
4534              
4535             }
4536             
4537             if (this.badge !== '') {
4538                  
4539                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4540             }
4541         }
4542         
4543         
4544         
4545         return cfg;
4546     },
4547     initEvents: function() 
4548     {
4549         if (typeof (this.menu) != 'undefined') {
4550             this.menu.parentType = this.xtype;
4551             this.menu.triggerEl = this.el;
4552             this.menu = this.addxtype(Roo.apply({}, this.menu));
4553         }
4554         
4555         this.el.select('a',true).on('click', this.onClick, this);
4556         
4557         if(this.tagtype == 'span'){
4558             this.el.select('span',true).on('click', this.onClick, this);
4559         }
4560        
4561         // at this point parent should be available..
4562         this.parent().register(this);
4563     },
4564     
4565     onClick : function(e)
4566     {
4567         if (e.getTarget('.dropdown-menu-item')) {
4568             // did you click on a menu itemm.... - then don't trigger onclick..
4569             return;
4570         }
4571         
4572         if(
4573                 this.preventDefault || 
4574                 this.href == '#' 
4575         ){
4576             Roo.log("NavItem - prevent Default?");
4577             e.preventDefault();
4578         }
4579         
4580         if (this.disabled) {
4581             return;
4582         }
4583         
4584         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4585         if (tg && tg.transition) {
4586             Roo.log("waiting for the transitionend");
4587             return;
4588         }
4589         
4590         
4591         
4592         //Roo.log("fire event clicked");
4593         if(this.fireEvent('click', this, e) === false){
4594             return;
4595         };
4596         
4597         if(this.tagtype == 'span'){
4598             return;
4599         }
4600         
4601         //Roo.log(this.href);
4602         var ael = this.el.select('a',true).first();
4603         //Roo.log(ael);
4604         
4605         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4606             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4607             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4608                 return; // ignore... - it's a 'hash' to another page.
4609             }
4610             Roo.log("NavItem - prevent Default?");
4611             e.preventDefault();
4612             this.scrollToElement(e);
4613         }
4614         
4615         
4616         var p =  this.parent();
4617    
4618         if (['tabs','pills'].indexOf(p.type)!==-1) {
4619             if (typeof(p.setActiveItem) !== 'undefined') {
4620                 p.setActiveItem(this);
4621             }
4622         }
4623         
4624         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4625         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4626             // remove the collapsed menu expand...
4627             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4628         }
4629     },
4630     
4631     isActive: function () {
4632         return this.active
4633     },
4634     setActive : function(state, fire, is_was_active)
4635     {
4636         if (this.active && !state && this.navId) {
4637             this.was_active = true;
4638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639             if (nv) {
4640                 nv.clearWasActive(this);
4641             }
4642             
4643         }
4644         this.active = state;
4645         
4646         if (!state ) {
4647             this.el.removeClass('active');
4648         } else if (!this.el.hasClass('active')) {
4649             this.el.addClass('active');
4650         }
4651         if (fire) {
4652             this.fireEvent('changed', this, state);
4653         }
4654         
4655         // show a panel if it's registered and related..
4656         
4657         if (!this.navId || !this.tabId || !state || is_was_active) {
4658             return;
4659         }
4660         
4661         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4662         if (!tg) {
4663             return;
4664         }
4665         var pan = tg.getPanelByName(this.tabId);
4666         if (!pan) {
4667             return;
4668         }
4669         // if we can not flip to new panel - go back to old nav highlight..
4670         if (false == tg.showPanel(pan)) {
4671             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672             if (nv) {
4673                 var onav = nv.getWasActive();
4674                 if (onav) {
4675                     onav.setActive(true, false, true);
4676                 }
4677             }
4678             
4679         }
4680         
4681         
4682         
4683     },
4684      // this should not be here...
4685     setDisabled : function(state)
4686     {
4687         this.disabled = state;
4688         if (!state ) {
4689             this.el.removeClass('disabled');
4690         } else if (!this.el.hasClass('disabled')) {
4691             this.el.addClass('disabled');
4692         }
4693         
4694     },
4695     
4696     /**
4697      * Fetch the element to display the tooltip on.
4698      * @return {Roo.Element} defaults to this.el
4699      */
4700     tooltipEl : function()
4701     {
4702         return this.el.select('' + this.tagtype + '', true).first();
4703     },
4704     
4705     scrollToElement : function(e)
4706     {
4707         var c = document.body;
4708         
4709         /*
4710          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711          */
4712         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4713             c = document.documentElement;
4714         }
4715         
4716         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4717         
4718         if(!target){
4719             return;
4720         }
4721
4722         var o = target.calcOffsetsTo(c);
4723         
4724         var options = {
4725             target : target,
4726             value : o[1]
4727         };
4728         
4729         this.fireEvent('scrollto', this, options, e);
4730         
4731         Roo.get(c).scrollTo('top', options.value, true);
4732         
4733         return;
4734     }
4735 });
4736  
4737
4738  /*
4739  * - LGPL
4740  *
4741  * sidebar item
4742  *
4743  *  li
4744  *    <span> icon </span>
4745  *    <span> text </span>
4746  *    <span>badge </span>
4747  */
4748
4749 /**
4750  * @class Roo.bootstrap.NavSidebarItem
4751  * @extends Roo.bootstrap.NavItem
4752  * Bootstrap Navbar.NavSidebarItem class
4753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4754  * {Boolean} open is the menu open
4755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4757  * {String} buttonSize (sm|md|lg)the extra classes for the button
4758  * {Boolean} showArrow show arrow next to the text (default true)
4759  * @constructor
4760  * Create a new Navbar Button
4761  * @param {Object} config The config object
4762  */
4763 Roo.bootstrap.NavSidebarItem = function(config){
4764     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4765     this.addEvents({
4766         // raw events
4767         /**
4768          * @event click
4769          * The raw click event for the entire grid.
4770          * @param {Roo.EventObject} e
4771          */
4772         "click" : true,
4773          /**
4774             * @event changed
4775             * Fires when the active item active state changes
4776             * @param {Roo.bootstrap.NavSidebarItem} this
4777             * @param {boolean} state the new state
4778              
4779          */
4780         'changed': true
4781     });
4782    
4783 };
4784
4785 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4786     
4787     badgeWeight : 'default',
4788     
4789     open: false,
4790     
4791     buttonView : false,
4792     
4793     buttonWeight : 'default',
4794     
4795     buttonSize : 'md',
4796     
4797     showArrow : true,
4798     
4799     getAutoCreate : function(){
4800         
4801         
4802         var a = {
4803                 tag: 'a',
4804                 href : this.href || '#',
4805                 cls: '',
4806                 html : '',
4807                 cn : []
4808         };
4809         
4810         if(this.buttonView){
4811             a = {
4812                 tag: 'button',
4813                 href : this.href || '#',
4814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4815                 html : this.html,
4816                 cn : []
4817             };
4818         }
4819         
4820         var cfg = {
4821             tag: 'li',
4822             cls: '',
4823             cn: [ a ]
4824         };
4825         
4826         if (this.active) {
4827             cfg.cls += ' active';
4828         }
4829         
4830         if (this.disabled) {
4831             cfg.cls += ' disabled';
4832         }
4833         if (this.open) {
4834             cfg.cls += ' open x-open';
4835         }
4836         // left icon..
4837         if (this.glyphicon || this.icon) {
4838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4839             a.cn.push({ tag : 'i', cls : c }) ;
4840         }
4841         
4842         if(!this.buttonView){
4843             var span = {
4844                 tag: 'span',
4845                 html : this.html || ''
4846             };
4847
4848             a.cn.push(span);
4849             
4850         }
4851         
4852         if (this.badge !== '') {
4853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4854         }
4855         
4856         if (this.menu) {
4857             
4858             if(this.showArrow){
4859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4860             }
4861             
4862             a.cls += ' dropdown-toggle treeview' ;
4863         }
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents : function()
4869     { 
4870         if (typeof (this.menu) != 'undefined') {
4871             this.menu.parentType = this.xtype;
4872             this.menu.triggerEl = this.el;
4873             this.menu = this.addxtype(Roo.apply({}, this.menu));
4874         }
4875         
4876         this.el.on('click', this.onClick, this);
4877         
4878         if(this.badge !== ''){
4879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4880         }
4881         
4882     },
4883     
4884     onClick : function(e)
4885     {
4886         if(this.disabled){
4887             e.preventDefault();
4888             return;
4889         }
4890         
4891         if(this.preventDefault){
4892             e.preventDefault();
4893         }
4894         
4895         this.fireEvent('click', this);
4896     },
4897     
4898     disable : function()
4899     {
4900         this.setDisabled(true);
4901     },
4902     
4903     enable : function()
4904     {
4905         this.setDisabled(false);
4906     },
4907     
4908     setDisabled : function(state)
4909     {
4910         if(this.disabled == state){
4911             return;
4912         }
4913         
4914         this.disabled = state;
4915         
4916         if (state) {
4917             this.el.addClass('disabled');
4918             return;
4919         }
4920         
4921         this.el.removeClass('disabled');
4922         
4923         return;
4924     },
4925     
4926     setActive : function(state)
4927     {
4928         if(this.active == state){
4929             return;
4930         }
4931         
4932         this.active = state;
4933         
4934         if (state) {
4935             this.el.addClass('active');
4936             return;
4937         }
4938         
4939         this.el.removeClass('active');
4940         
4941         return;
4942     },
4943     
4944     isActive: function () 
4945     {
4946         return this.active;
4947     },
4948     
4949     setBadge : function(str)
4950     {
4951         if(!this.badgeEl){
4952             return;
4953         }
4954         
4955         this.badgeEl.dom.innerHTML = str;
4956     }
4957     
4958    
4959      
4960  
4961 });
4962  
4963
4964  /*
4965  * - LGPL
4966  *
4967  * row
4968  * 
4969  */
4970
4971 /**
4972  * @class Roo.bootstrap.Row
4973  * @extends Roo.bootstrap.Component
4974  * Bootstrap Row class (contains columns...)
4975  * 
4976  * @constructor
4977  * Create a new Row
4978  * @param {Object} config The config object
4979  */
4980
4981 Roo.bootstrap.Row = function(config){
4982     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4983 };
4984
4985 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4986     
4987     getAutoCreate : function(){
4988        return {
4989             cls: 'row clearfix'
4990        };
4991     }
4992     
4993     
4994 });
4995
4996  
4997
4998  /*
4999  * - LGPL
5000  *
5001  * element
5002  * 
5003  */
5004
5005 /**
5006  * @class Roo.bootstrap.Element
5007  * @extends Roo.bootstrap.Component
5008  * Bootstrap Element class
5009  * @cfg {String} html contents of the element
5010  * @cfg {String} tag tag of the element
5011  * @cfg {String} cls class of the element
5012  * @cfg {Boolean} preventDefault (true|false) default false
5013  * @cfg {Boolean} clickable (true|false) default false
5014  * 
5015  * @constructor
5016  * Create a new Element
5017  * @param {Object} config The config object
5018  */
5019
5020 Roo.bootstrap.Element = function(config){
5021     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5022     
5023     this.addEvents({
5024         // raw events
5025         /**
5026          * @event click
5027          * When a element is chick
5028          * @param {Roo.bootstrap.Element} this
5029          * @param {Roo.EventObject} e
5030          */
5031         "click" : true
5032     });
5033 };
5034
5035 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5036     
5037     tag: 'div',
5038     cls: '',
5039     html: '',
5040     preventDefault: false, 
5041     clickable: false,
5042     
5043     getAutoCreate : function(){
5044         
5045         var cfg = {
5046             tag: this.tag,
5047             // cls: this.cls, double assign in parent class Component.js :: onRender
5048             html: this.html
5049         };
5050         
5051         return cfg;
5052     },
5053     
5054     initEvents: function() 
5055     {
5056         Roo.bootstrap.Element.superclass.initEvents.call(this);
5057         
5058         if(this.clickable){
5059             this.el.on('click', this.onClick, this);
5060         }
5061         
5062     },
5063     
5064     onClick : function(e)
5065     {
5066         if(this.preventDefault){
5067             e.preventDefault();
5068         }
5069         
5070         this.fireEvent('click', this, e);
5071     },
5072     
5073     getValue : function()
5074     {
5075         return this.el.dom.innerHTML;
5076     },
5077     
5078     setValue : function(value)
5079     {
5080         this.el.dom.innerHTML = value;
5081     }
5082    
5083 });
5084
5085  
5086
5087  /*
5088  * - LGPL
5089  *
5090  * pagination
5091  * 
5092  */
5093
5094 /**
5095  * @class Roo.bootstrap.Pagination
5096  * @extends Roo.bootstrap.Component
5097  * Bootstrap Pagination class
5098  * @cfg {String} size xs | sm | md | lg
5099  * @cfg {Boolean} inverse false | true
5100  * 
5101  * @constructor
5102  * Create a new Pagination
5103  * @param {Object} config The config object
5104  */
5105
5106 Roo.bootstrap.Pagination = function(config){
5107     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5108 };
5109
5110 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5111     
5112     cls: false,
5113     size: false,
5114     inverse: false,
5115     
5116     getAutoCreate : function(){
5117         var cfg = {
5118             tag: 'ul',
5119                 cls: 'pagination'
5120         };
5121         if (this.inverse) {
5122             cfg.cls += ' inverse';
5123         }
5124         if (this.html) {
5125             cfg.html=this.html;
5126         }
5127         if (this.cls) {
5128             cfg.cls += " " + this.cls;
5129         }
5130         return cfg;
5131     }
5132    
5133 });
5134
5135  
5136
5137  /*
5138  * - LGPL
5139  *
5140  * Pagination item
5141  * 
5142  */
5143
5144
5145 /**
5146  * @class Roo.bootstrap.PaginationItem
5147  * @extends Roo.bootstrap.Component
5148  * Bootstrap PaginationItem class
5149  * @cfg {String} html text
5150  * @cfg {String} href the link
5151  * @cfg {Boolean} preventDefault (true | false) default true
5152  * @cfg {Boolean} active (true | false) default false
5153  * @cfg {Boolean} disabled default false
5154  * 
5155  * 
5156  * @constructor
5157  * Create a new PaginationItem
5158  * @param {Object} config The config object
5159  */
5160
5161
5162 Roo.bootstrap.PaginationItem = function(config){
5163     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5164     this.addEvents({
5165         // raw events
5166         /**
5167          * @event click
5168          * The raw click event for the entire grid.
5169          * @param {Roo.EventObject} e
5170          */
5171         "click" : true
5172     });
5173 };
5174
5175 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5176     
5177     href : false,
5178     html : false,
5179     preventDefault: true,
5180     active : false,
5181     cls : false,
5182     disabled: false,
5183     
5184     getAutoCreate : function(){
5185         var cfg= {
5186             tag: 'li',
5187             cn: [
5188                 {
5189                     tag : 'a',
5190                     href : this.href ? this.href : '#',
5191                     html : this.html ? this.html : ''
5192                 }
5193             ]
5194         };
5195         
5196         if(this.cls){
5197             cfg.cls = this.cls;
5198         }
5199         
5200         if(this.disabled){
5201             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5202         }
5203         
5204         if(this.active){
5205             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5206         }
5207         
5208         return cfg;
5209     },
5210     
5211     initEvents: function() {
5212         
5213         this.el.on('click', this.onClick, this);
5214         
5215     },
5216     onClick : function(e)
5217     {
5218         Roo.log('PaginationItem on click ');
5219         if(this.preventDefault){
5220             e.preventDefault();
5221         }
5222         
5223         if(this.disabled){
5224             return;
5225         }
5226         
5227         this.fireEvent('click', this, e);
5228     }
5229    
5230 });
5231
5232  
5233
5234  /*
5235  * - LGPL
5236  *
5237  * slider
5238  * 
5239  */
5240
5241
5242 /**
5243  * @class Roo.bootstrap.Slider
5244  * @extends Roo.bootstrap.Component
5245  * Bootstrap Slider class
5246  *    
5247  * @constructor
5248  * Create a new Slider
5249  * @param {Object} config The config object
5250  */
5251
5252 Roo.bootstrap.Slider = function(config){
5253     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5254 };
5255
5256 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5257     
5258     getAutoCreate : function(){
5259         
5260         var cfg = {
5261             tag: 'div',
5262             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5263             cn: [
5264                 {
5265                     tag: 'a',
5266                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5267                 }
5268             ]
5269         };
5270         
5271         return cfg;
5272     }
5273    
5274 });
5275
5276  /*
5277  * Based on:
5278  * Ext JS Library 1.1.1
5279  * Copyright(c) 2006-2007, Ext JS, LLC.
5280  *
5281  * Originally Released Under LGPL - original licence link has changed is not relivant.
5282  *
5283  * Fork - LGPL
5284  * <script type="text/javascript">
5285  */
5286  
5287
5288 /**
5289  * @class Roo.grid.ColumnModel
5290  * @extends Roo.util.Observable
5291  * This is the default implementation of a ColumnModel used by the Grid. It defines
5292  * the columns in the grid.
5293  * <br>Usage:<br>
5294  <pre><code>
5295  var colModel = new Roo.grid.ColumnModel([
5296         {header: "Ticker", width: 60, sortable: true, locked: true},
5297         {header: "Company Name", width: 150, sortable: true},
5298         {header: "Market Cap.", width: 100, sortable: true},
5299         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5300         {header: "Employees", width: 100, sortable: true, resizable: false}
5301  ]);
5302  </code></pre>
5303  * <p>
5304  
5305  * The config options listed for this class are options which may appear in each
5306  * individual column definition.
5307  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308  * @constructor
5309  * @param {Object} config An Array of column config objects. See this class's
5310  * config objects for details.
5311 */
5312 Roo.grid.ColumnModel = function(config){
5313         /**
5314      * The config passed into the constructor
5315      */
5316     this.config = config;
5317     this.lookup = {};
5318
5319     // if no id, create one
5320     // if the column does not have a dataIndex mapping,
5321     // map it to the order it is in the config
5322     for(var i = 0, len = config.length; i < len; i++){
5323         var c = config[i];
5324         if(typeof c.dataIndex == "undefined"){
5325             c.dataIndex = i;
5326         }
5327         if(typeof c.renderer == "string"){
5328             c.renderer = Roo.util.Format[c.renderer];
5329         }
5330         if(typeof c.id == "undefined"){
5331             c.id = Roo.id();
5332         }
5333         if(c.editor && c.editor.xtype){
5334             c.editor  = Roo.factory(c.editor, Roo.grid);
5335         }
5336         if(c.editor && c.editor.isFormField){
5337             c.editor = new Roo.grid.GridEditor(c.editor);
5338         }
5339         this.lookup[c.id] = c;
5340     }
5341
5342     /**
5343      * The width of columns which have no width specified (defaults to 100)
5344      * @type Number
5345      */
5346     this.defaultWidth = 100;
5347
5348     /**
5349      * Default sortable of columns which have no sortable specified (defaults to false)
5350      * @type Boolean
5351      */
5352     this.defaultSortable = false;
5353
5354     this.addEvents({
5355         /**
5356              * @event widthchange
5357              * Fires when the width of a column changes.
5358              * @param {ColumnModel} this
5359              * @param {Number} columnIndex The column index
5360              * @param {Number} newWidth The new width
5361              */
5362             "widthchange": true,
5363         /**
5364              * @event headerchange
5365              * Fires when the text of a header changes.
5366              * @param {ColumnModel} this
5367              * @param {Number} columnIndex The column index
5368              * @param {Number} newText The new header text
5369              */
5370             "headerchange": true,
5371         /**
5372              * @event hiddenchange
5373              * Fires when a column is hidden or "unhidden".
5374              * @param {ColumnModel} this
5375              * @param {Number} columnIndex The column index
5376              * @param {Boolean} hidden true if hidden, false otherwise
5377              */
5378             "hiddenchange": true,
5379             /**
5380          * @event columnmoved
5381          * Fires when a column is moved.
5382          * @param {ColumnModel} this
5383          * @param {Number} oldIndex
5384          * @param {Number} newIndex
5385          */
5386         "columnmoved" : true,
5387         /**
5388          * @event columlockchange
5389          * Fires when a column's locked state is changed
5390          * @param {ColumnModel} this
5391          * @param {Number} colIndex
5392          * @param {Boolean} locked true if locked
5393          */
5394         "columnlockchange" : true
5395     });
5396     Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 };
5398 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399     /**
5400      * @cfg {String} header The header text to display in the Grid view.
5401      */
5402     /**
5403      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5404      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5405      * specified, the column's index is used as an index into the Record's data Array.
5406      */
5407     /**
5408      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5409      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5410      */
5411     /**
5412      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5413      * Defaults to the value of the {@link #defaultSortable} property.
5414      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5415      */
5416     /**
5417      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5418      */
5419     /**
5420      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5421      */
5422     /**
5423      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5424      */
5425     /**
5426      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5427      */
5428     /**
5429      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5430      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5431      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5432      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5433      */
5434        /**
5435      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5436      */
5437     /**
5438      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5439      */
5440     /**
5441      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5442      */
5443     /**
5444      * @cfg {String} cursor (Optional)
5445      */
5446     /**
5447      * @cfg {String} tooltip (Optional)
5448      */
5449     /**
5450      * @cfg {Number} xs (Optional)
5451      */
5452     /**
5453      * @cfg {Number} sm (Optional)
5454      */
5455     /**
5456      * @cfg {Number} md (Optional)
5457      */
5458     /**
5459      * @cfg {Number} lg (Optional)
5460      */
5461     /**
5462      * Returns the id of the column at the specified index.
5463      * @param {Number} index The column index
5464      * @return {String} the id
5465      */
5466     getColumnId : function(index){
5467         return this.config[index].id;
5468     },
5469
5470     /**
5471      * Returns the column for a specified id.
5472      * @param {String} id The column id
5473      * @return {Object} the column
5474      */
5475     getColumnById : function(id){
5476         return this.lookup[id];
5477     },
5478
5479     
5480     /**
5481      * Returns the column for a specified dataIndex.
5482      * @param {String} dataIndex The column dataIndex
5483      * @return {Object|Boolean} the column or false if not found
5484      */
5485     getColumnByDataIndex: function(dataIndex){
5486         var index = this.findColumnIndex(dataIndex);
5487         return index > -1 ? this.config[index] : false;
5488     },
5489     
5490     /**
5491      * Returns the index for a specified column id.
5492      * @param {String} id The column id
5493      * @return {Number} the index, or -1 if not found
5494      */
5495     getIndexById : function(id){
5496         for(var i = 0, len = this.config.length; i < len; i++){
5497             if(this.config[i].id == id){
5498                 return i;
5499             }
5500         }
5501         return -1;
5502     },
5503     
5504     /**
5505      * Returns the index for a specified column dataIndex.
5506      * @param {String} dataIndex The column dataIndex
5507      * @return {Number} the index, or -1 if not found
5508      */
5509     
5510     findColumnIndex : function(dataIndex){
5511         for(var i = 0, len = this.config.length; i < len; i++){
5512             if(this.config[i].dataIndex == dataIndex){
5513                 return i;
5514             }
5515         }
5516         return -1;
5517     },
5518     
5519     
5520     moveColumn : function(oldIndex, newIndex){
5521         var c = this.config[oldIndex];
5522         this.config.splice(oldIndex, 1);
5523         this.config.splice(newIndex, 0, c);
5524         this.dataMap = null;
5525         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5526     },
5527
5528     isLocked : function(colIndex){
5529         return this.config[colIndex].locked === true;
5530     },
5531
5532     setLocked : function(colIndex, value, suppressEvent){
5533         if(this.isLocked(colIndex) == value){
5534             return;
5535         }
5536         this.config[colIndex].locked = value;
5537         if(!suppressEvent){
5538             this.fireEvent("columnlockchange", this, colIndex, value);
5539         }
5540     },
5541
5542     getTotalLockedWidth : function(){
5543         var totalWidth = 0;
5544         for(var i = 0; i < this.config.length; i++){
5545             if(this.isLocked(i) && !this.isHidden(i)){
5546                 this.totalWidth += this.getColumnWidth(i);
5547             }
5548         }
5549         return totalWidth;
5550     },
5551
5552     getLockedCount : function(){
5553         for(var i = 0, len = this.config.length; i < len; i++){
5554             if(!this.isLocked(i)){
5555                 return i;
5556             }
5557         }
5558         
5559         return this.config.length;
5560     },
5561
5562     /**
5563      * Returns the number of columns.
5564      * @return {Number}
5565      */
5566     getColumnCount : function(visibleOnly){
5567         if(visibleOnly === true){
5568             var c = 0;
5569             for(var i = 0, len = this.config.length; i < len; i++){
5570                 if(!this.isHidden(i)){
5571                     c++;
5572                 }
5573             }
5574             return c;
5575         }
5576         return this.config.length;
5577     },
5578
5579     /**
5580      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5581      * @param {Function} fn
5582      * @param {Object} scope (optional)
5583      * @return {Array} result
5584      */
5585     getColumnsBy : function(fn, scope){
5586         var r = [];
5587         for(var i = 0, len = this.config.length; i < len; i++){
5588             var c = this.config[i];
5589             if(fn.call(scope||this, c, i) === true){
5590                 r[r.length] = c;
5591             }
5592         }
5593         return r;
5594     },
5595
5596     /**
5597      * Returns true if the specified column is sortable.
5598      * @param {Number} col The column index
5599      * @return {Boolean}
5600      */
5601     isSortable : function(col){
5602         if(typeof this.config[col].sortable == "undefined"){
5603             return this.defaultSortable;
5604         }
5605         return this.config[col].sortable;
5606     },
5607
5608     /**
5609      * Returns the rendering (formatting) function defined for the column.
5610      * @param {Number} col The column index.
5611      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612      */
5613     getRenderer : function(col){
5614         if(!this.config[col].renderer){
5615             return Roo.grid.ColumnModel.defaultRenderer;
5616         }
5617         return this.config[col].renderer;
5618     },
5619
5620     /**
5621      * Sets the rendering (formatting) function for a column.
5622      * @param {Number} col The column index
5623      * @param {Function} fn The function to use to process the cell's raw data
5624      * to return HTML markup for the grid view. The render function is called with
5625      * the following parameters:<ul>
5626      * <li>Data value.</li>
5627      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5628      * <li>css A CSS style string to apply to the table cell.</li>
5629      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5630      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5631      * <li>Row index</li>
5632      * <li>Column index</li>
5633      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634      */
5635     setRenderer : function(col, fn){
5636         this.config[col].renderer = fn;
5637     },
5638
5639     /**
5640      * Returns the width for the specified column.
5641      * @param {Number} col The column index
5642      * @return {Number}
5643      */
5644     getColumnWidth : function(col){
5645         return this.config[col].width * 1 || this.defaultWidth;
5646     },
5647
5648     /**
5649      * Sets the width for a column.
5650      * @param {Number} col The column index
5651      * @param {Number} width The new width
5652      */
5653     setColumnWidth : function(col, width, suppressEvent){
5654         this.config[col].width = width;
5655         this.totalWidth = null;
5656         if(!suppressEvent){
5657              this.fireEvent("widthchange", this, col, width);
5658         }
5659     },
5660
5661     /**
5662      * Returns the total width of all columns.
5663      * @param {Boolean} includeHidden True to include hidden column widths
5664      * @return {Number}
5665      */
5666     getTotalWidth : function(includeHidden){
5667         if(!this.totalWidth){
5668             this.totalWidth = 0;
5669             for(var i = 0, len = this.config.length; i < len; i++){
5670                 if(includeHidden || !this.isHidden(i)){
5671                     this.totalWidth += this.getColumnWidth(i);
5672                 }
5673             }
5674         }
5675         return this.totalWidth;
5676     },
5677
5678     /**
5679      * Returns the header for the specified column.
5680      * @param {Number} col The column index
5681      * @return {String}
5682      */
5683     getColumnHeader : function(col){
5684         return this.config[col].header;
5685     },
5686
5687     /**
5688      * Sets the header for a column.
5689      * @param {Number} col The column index
5690      * @param {String} header The new header
5691      */
5692     setColumnHeader : function(col, header){
5693         this.config[col].header = header;
5694         this.fireEvent("headerchange", this, col, header);
5695     },
5696
5697     /**
5698      * Returns the tooltip for the specified column.
5699      * @param {Number} col The column index
5700      * @return {String}
5701      */
5702     getColumnTooltip : function(col){
5703             return this.config[col].tooltip;
5704     },
5705     /**
5706      * Sets the tooltip for a column.
5707      * @param {Number} col The column index
5708      * @param {String} tooltip The new tooltip
5709      */
5710     setColumnTooltip : function(col, tooltip){
5711             this.config[col].tooltip = tooltip;
5712     },
5713
5714     /**
5715      * Returns the dataIndex for the specified column.
5716      * @param {Number} col The column index
5717      * @return {Number}
5718      */
5719     getDataIndex : function(col){
5720         return this.config[col].dataIndex;
5721     },
5722
5723     /**
5724      * Sets the dataIndex for a column.
5725      * @param {Number} col The column index
5726      * @param {Number} dataIndex The new dataIndex
5727      */
5728     setDataIndex : function(col, dataIndex){
5729         this.config[col].dataIndex = dataIndex;
5730     },
5731
5732     
5733     
5734     /**
5735      * Returns true if the cell is editable.
5736      * @param {Number} colIndex The column index
5737      * @param {Number} rowIndex The row index - this is nto actually used..?
5738      * @return {Boolean}
5739      */
5740     isCellEditable : function(colIndex, rowIndex){
5741         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5742     },
5743
5744     /**
5745      * Returns the editor defined for the cell/column.
5746      * return false or null to disable editing.
5747      * @param {Number} colIndex The column index
5748      * @param {Number} rowIndex The row index
5749      * @return {Object}
5750      */
5751     getCellEditor : function(colIndex, rowIndex){
5752         return this.config[colIndex].editor;
5753     },
5754
5755     /**
5756      * Sets if a column is editable.
5757      * @param {Number} col The column index
5758      * @param {Boolean} editable True if the column is editable
5759      */
5760     setEditable : function(col, editable){
5761         this.config[col].editable = editable;
5762     },
5763
5764
5765     /**
5766      * Returns true if the column is hidden.
5767      * @param {Number} colIndex The column index
5768      * @return {Boolean}
5769      */
5770     isHidden : function(colIndex){
5771         return this.config[colIndex].hidden;
5772     },
5773
5774
5775     /**
5776      * Returns true if the column width cannot be changed
5777      */
5778     isFixed : function(colIndex){
5779         return this.config[colIndex].fixed;
5780     },
5781
5782     /**
5783      * Returns true if the column can be resized
5784      * @return {Boolean}
5785      */
5786     isResizable : function(colIndex){
5787         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5788     },
5789     /**
5790      * Sets if a column is hidden.
5791      * @param {Number} colIndex The column index
5792      * @param {Boolean} hidden True if the column is hidden
5793      */
5794     setHidden : function(colIndex, hidden){
5795         this.config[colIndex].hidden = hidden;
5796         this.totalWidth = null;
5797         this.fireEvent("hiddenchange", this, colIndex, hidden);
5798     },
5799
5800     /**
5801      * Sets the editor for a column.
5802      * @param {Number} col The column index
5803      * @param {Object} editor The editor object
5804      */
5805     setEditor : function(col, editor){
5806         this.config[col].editor = editor;
5807     }
5808 });
5809
5810 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 {
5812     if(typeof value == "object") {
5813         return value;
5814     }
5815         if(typeof value == "string" && value.length < 1){
5816             return "&#160;";
5817         }
5818     
5819         return String.format("{0}", value);
5820 };
5821
5822 // Alias for backwards compatibility
5823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5824 /*
5825  * Based on:
5826  * Ext JS Library 1.1.1
5827  * Copyright(c) 2006-2007, Ext JS, LLC.
5828  *
5829  * Originally Released Under LGPL - original licence link has changed is not relivant.
5830  *
5831  * Fork - LGPL
5832  * <script type="text/javascript">
5833  */
5834  
5835 /**
5836  * @class Roo.LoadMask
5837  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5838  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5839  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5840  * element's UpdateManager load indicator and will be destroyed after the initial load.
5841  * @constructor
5842  * Create a new LoadMask
5843  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5844  * @param {Object} config The config object
5845  */
5846 Roo.LoadMask = function(el, config){
5847     this.el = Roo.get(el);
5848     Roo.apply(this, config);
5849     if(this.store){
5850         this.store.on('beforeload', this.onBeforeLoad, this);
5851         this.store.on('load', this.onLoad, this);
5852         this.store.on('loadexception', this.onLoadException, this);
5853         this.removeMask = false;
5854     }else{
5855         var um = this.el.getUpdateManager();
5856         um.showLoadIndicator = false; // disable the default indicator
5857         um.on('beforeupdate', this.onBeforeLoad, this);
5858         um.on('update', this.onLoad, this);
5859         um.on('failure', this.onLoad, this);
5860         this.removeMask = true;
5861     }
5862 };
5863
5864 Roo.LoadMask.prototype = {
5865     /**
5866      * @cfg {Boolean} removeMask
5867      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5868      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5869      */
5870     /**
5871      * @cfg {String} msg
5872      * The text to display in a centered loading message box (defaults to 'Loading...')
5873      */
5874     msg : 'Loading...',
5875     /**
5876      * @cfg {String} msgCls
5877      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878      */
5879     msgCls : 'x-mask-loading',
5880
5881     /**
5882      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5883      * @type Boolean
5884      */
5885     disabled: false,
5886
5887     /**
5888      * Disables the mask to prevent it from being displayed
5889      */
5890     disable : function(){
5891        this.disabled = true;
5892     },
5893
5894     /**
5895      * Enables the mask so that it can be displayed
5896      */
5897     enable : function(){
5898         this.disabled = false;
5899     },
5900     
5901     onLoadException : function()
5902     {
5903         Roo.log(arguments);
5904         
5905         if (typeof(arguments[3]) != 'undefined') {
5906             Roo.MessageBox.alert("Error loading",arguments[3]);
5907         } 
5908         /*
5909         try {
5910             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5911                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5912             }   
5913         } catch(e) {
5914             
5915         }
5916         */
5917     
5918         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5919     },
5920     // private
5921     onLoad : function()
5922     {
5923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5924     },
5925
5926     // private
5927     onBeforeLoad : function(){
5928         if(!this.disabled){
5929             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5930         }
5931     },
5932
5933     // private
5934     destroy : function(){
5935         if(this.store){
5936             this.store.un('beforeload', this.onBeforeLoad, this);
5937             this.store.un('load', this.onLoad, this);
5938             this.store.un('loadexception', this.onLoadException, this);
5939         }else{
5940             var um = this.el.getUpdateManager();
5941             um.un('beforeupdate', this.onBeforeLoad, this);
5942             um.un('update', this.onLoad, this);
5943             um.un('failure', this.onLoad, this);
5944         }
5945     }
5946 };/*
5947  * - LGPL
5948  *
5949  * table
5950  * 
5951  */
5952
5953 /**
5954  * @class Roo.bootstrap.Table
5955  * @extends Roo.bootstrap.Component
5956  * Bootstrap Table class
5957  * @cfg {String} cls table class
5958  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5959  * @cfg {String} bgcolor Specifies the background color for a table
5960  * @cfg {Number} border Specifies whether the table cells should have borders or not
5961  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5962  * @cfg {Number} cellspacing Specifies the space between cells
5963  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5964  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5965  * @cfg {String} sortable Specifies that the table should be sortable
5966  * @cfg {String} summary Specifies a summary of the content of a table
5967  * @cfg {Number} width Specifies the width of a table
5968  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969  * 
5970  * @cfg {boolean} striped Should the rows be alternative striped
5971  * @cfg {boolean} bordered Add borders to the table
5972  * @cfg {boolean} hover Add hover highlighting
5973  * @cfg {boolean} condensed Format condensed
5974  * @cfg {boolean} responsive Format condensed
5975  * @cfg {Boolean} loadMask (true|false) default false
5976  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5977  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5978  * @cfg {Boolean} rowSelection (true|false) default false
5979  * @cfg {Boolean} cellSelection (true|false) default false
5980  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5981  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5982  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5983  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5984  
5985  * 
5986  * @constructor
5987  * Create a new Table
5988  * @param {Object} config The config object
5989  */
5990
5991 Roo.bootstrap.Table = function(config){
5992     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5993     
5994   
5995     
5996     // BC...
5997     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5998     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5999     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6000     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001     
6002     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003     if (this.sm) {
6004         this.sm.grid = this;
6005         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6006         this.sm = this.selModel;
6007         this.sm.xmodule = this.xmodule || false;
6008     }
6009     
6010     if (this.cm && typeof(this.cm.config) == 'undefined') {
6011         this.colModel = new Roo.grid.ColumnModel(this.cm);
6012         this.cm = this.colModel;
6013         this.cm.xmodule = this.xmodule || false;
6014     }
6015     if (this.store) {
6016         this.store= Roo.factory(this.store, Roo.data);
6017         this.ds = this.store;
6018         this.ds.xmodule = this.xmodule || false;
6019          
6020     }
6021     if (this.footer && this.store) {
6022         this.footer.dataSource = this.ds;
6023         this.footer = Roo.factory(this.footer);
6024     }
6025     
6026     /** @private */
6027     this.addEvents({
6028         /**
6029          * @event cellclick
6030          * Fires when a cell is clicked
6031          * @param {Roo.bootstrap.Table} this
6032          * @param {Roo.Element} el
6033          * @param {Number} rowIndex
6034          * @param {Number} columnIndex
6035          * @param {Roo.EventObject} e
6036          */
6037         "cellclick" : true,
6038         /**
6039          * @event celldblclick
6040          * Fires when a cell is double clicked
6041          * @param {Roo.bootstrap.Table} this
6042          * @param {Roo.Element} el
6043          * @param {Number} rowIndex
6044          * @param {Number} columnIndex
6045          * @param {Roo.EventObject} e
6046          */
6047         "celldblclick" : true,
6048         /**
6049          * @event rowclick
6050          * Fires when a row is clicked
6051          * @param {Roo.bootstrap.Table} this
6052          * @param {Roo.Element} el
6053          * @param {Number} rowIndex
6054          * @param {Roo.EventObject} e
6055          */
6056         "rowclick" : true,
6057         /**
6058          * @event rowdblclick
6059          * Fires when a row is double clicked
6060          * @param {Roo.bootstrap.Table} this
6061          * @param {Roo.Element} el
6062          * @param {Number} rowIndex
6063          * @param {Roo.EventObject} e
6064          */
6065         "rowdblclick" : true,
6066         /**
6067          * @event mouseover
6068          * Fires when a mouseover occur
6069          * @param {Roo.bootstrap.Table} this
6070          * @param {Roo.Element} el
6071          * @param {Number} rowIndex
6072          * @param {Number} columnIndex
6073          * @param {Roo.EventObject} e
6074          */
6075         "mouseover" : true,
6076         /**
6077          * @event mouseout
6078          * Fires when a mouseout occur
6079          * @param {Roo.bootstrap.Table} this
6080          * @param {Roo.Element} el
6081          * @param {Number} rowIndex
6082          * @param {Number} columnIndex
6083          * @param {Roo.EventObject} e
6084          */
6085         "mouseout" : true,
6086         /**
6087          * @event rowclass
6088          * Fires when a row is rendered, so you can change add a style to it.
6089          * @param {Roo.bootstrap.Table} this
6090          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6091          */
6092         'rowclass' : true,
6093           /**
6094          * @event rowsrendered
6095          * Fires when all the  rows have been rendered
6096          * @param {Roo.bootstrap.Table} this
6097          */
6098         'rowsrendered' : true,
6099         /**
6100          * @event contextmenu
6101          * The raw contextmenu event for the entire grid.
6102          * @param {Roo.EventObject} e
6103          */
6104         "contextmenu" : true,
6105         /**
6106          * @event rowcontextmenu
6107          * Fires when a row is right clicked
6108          * @param {Roo.bootstrap.Table} this
6109          * @param {Number} rowIndex
6110          * @param {Roo.EventObject} e
6111          */
6112         "rowcontextmenu" : true,
6113         /**
6114          * @event cellcontextmenu
6115          * Fires when a cell is right clicked
6116          * @param {Roo.bootstrap.Table} this
6117          * @param {Number} rowIndex
6118          * @param {Number} cellIndex
6119          * @param {Roo.EventObject} e
6120          */
6121          "cellcontextmenu" : true,
6122          /**
6123          * @event headercontextmenu
6124          * Fires when a header is right clicked
6125          * @param {Roo.bootstrap.Table} this
6126          * @param {Number} columnIndex
6127          * @param {Roo.EventObject} e
6128          */
6129         "headercontextmenu" : true
6130     });
6131 };
6132
6133 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6134     
6135     cls: false,
6136     align: false,
6137     bgcolor: false,
6138     border: false,
6139     cellpadding: false,
6140     cellspacing: false,
6141     frame: false,
6142     rules: false,
6143     sortable: false,
6144     summary: false,
6145     width: false,
6146     striped : false,
6147     scrollBody : false,
6148     bordered: false,
6149     hover:  false,
6150     condensed : false,
6151     responsive : false,
6152     sm : false,
6153     cm : false,
6154     store : false,
6155     loadMask : false,
6156     footerShow : true,
6157     headerShow : true,
6158   
6159     rowSelection : false,
6160     cellSelection : false,
6161     layout : false,
6162     
6163     // Roo.Element - the tbody
6164     mainBody: false,
6165     // Roo.Element - thead element
6166     mainHead: false,
6167     
6168     container: false, // used by gridpanel...
6169     
6170     lazyLoad : false,
6171     
6172     CSS : Roo.util.CSS,
6173     
6174     auto_hide_footer : false,
6175     
6176     getAutoCreate : function()
6177     {
6178         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6179         
6180         cfg = {
6181             tag: 'table',
6182             cls : 'table',
6183             cn : []
6184         };
6185         if (this.scrollBody) {
6186             cfg.cls += ' table-body-fixed';
6187         }    
6188         if (this.striped) {
6189             cfg.cls += ' table-striped';
6190         }
6191         
6192         if (this.hover) {
6193             cfg.cls += ' table-hover';
6194         }
6195         if (this.bordered) {
6196             cfg.cls += ' table-bordered';
6197         }
6198         if (this.condensed) {
6199             cfg.cls += ' table-condensed';
6200         }
6201         if (this.responsive) {
6202             cfg.cls += ' table-responsive';
6203         }
6204         
6205         if (this.cls) {
6206             cfg.cls+=  ' ' +this.cls;
6207         }
6208         
6209         // this lot should be simplifed...
6210         var _t = this;
6211         var cp = [
6212             'align',
6213             'bgcolor',
6214             'border',
6215             'cellpadding',
6216             'cellspacing',
6217             'frame',
6218             'rules',
6219             'sortable',
6220             'summary',
6221             'width'
6222         ].forEach(function(k) {
6223             if (_t[k]) {
6224                 cfg[k] = _t[k];
6225             }
6226         });
6227         
6228         
6229         if (this.layout) {
6230             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6231         }
6232         
6233         if(this.store || this.cm){
6234             if(this.headerShow){
6235                 cfg.cn.push(this.renderHeader());
6236             }
6237             
6238             cfg.cn.push(this.renderBody());
6239             
6240             if(this.footerShow){
6241                 cfg.cn.push(this.renderFooter());
6242             }
6243             // where does this come from?
6244             //cfg.cls+=  ' TableGrid';
6245         }
6246         
6247         return { cn : [ cfg ] };
6248     },
6249     
6250     initEvents : function()
6251     {   
6252         if(!this.store || !this.cm){
6253             return;
6254         }
6255         if (this.selModel) {
6256             this.selModel.initEvents();
6257         }
6258         
6259         
6260         //Roo.log('initEvents with ds!!!!');
6261         
6262         this.mainBody = this.el.select('tbody', true).first();
6263         this.mainHead = this.el.select('thead', true).first();
6264         this.mainFoot = this.el.select('tfoot', true).first();
6265         
6266         
6267         
6268         var _this = this;
6269         
6270         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6271             e.on('click', _this.sort, _this);
6272         });
6273         
6274         this.mainBody.on("click", this.onClick, this);
6275         this.mainBody.on("dblclick", this.onDblClick, this);
6276         
6277         // why is this done????? = it breaks dialogs??
6278         //this.parent().el.setStyle('position', 'relative');
6279         
6280         
6281         if (this.footer) {
6282             this.footer.parentId = this.id;
6283             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6284             
6285             if(this.lazyLoad){
6286                 this.el.select('tfoot tr td').first().addClass('hide');
6287             }
6288         } 
6289         
6290         if(this.loadMask) {
6291             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6292         }
6293         
6294         this.store.on('load', this.onLoad, this);
6295         this.store.on('beforeload', this.onBeforeLoad, this);
6296         this.store.on('update', this.onUpdate, this);
6297         this.store.on('add', this.onAdd, this);
6298         this.store.on("clear", this.clear, this);
6299         
6300         this.el.on("contextmenu", this.onContextMenu, this);
6301         
6302         this.mainBody.on('scroll', this.onBodyScroll, this);
6303         
6304         this.cm.on("headerchange", this.onHeaderChange, this);
6305         
6306         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6307         
6308     },
6309     
6310     onContextMenu : function(e, t)
6311     {
6312         this.processEvent("contextmenu", e);
6313     },
6314     
6315     processEvent : function(name, e)
6316     {
6317         if (name != 'touchstart' ) {
6318             this.fireEvent(name, e);    
6319         }
6320         
6321         var t = e.getTarget();
6322         
6323         var cell = Roo.get(t);
6324         
6325         if(!cell){
6326             return;
6327         }
6328         
6329         if(cell.findParent('tfoot', false, true)){
6330             return;
6331         }
6332         
6333         if(cell.findParent('thead', false, true)){
6334             
6335             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6336                 cell = Roo.get(t).findParent('th', false, true);
6337                 if (!cell) {
6338                     Roo.log("failed to find th in thead?");
6339                     Roo.log(e.getTarget());
6340                     return;
6341                 }
6342             }
6343             
6344             var cellIndex = cell.dom.cellIndex;
6345             
6346             var ename = name == 'touchstart' ? 'click' : name;
6347             this.fireEvent("header" + ename, this, cellIndex, e);
6348             
6349             return;
6350         }
6351         
6352         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353             cell = Roo.get(t).findParent('td', false, true);
6354             if (!cell) {
6355                 Roo.log("failed to find th in tbody?");
6356                 Roo.log(e.getTarget());
6357                 return;
6358             }
6359         }
6360         
6361         var row = cell.findParent('tr', false, true);
6362         var cellIndex = cell.dom.cellIndex;
6363         var rowIndex = row.dom.rowIndex - 1;
6364         
6365         if(row !== false){
6366             
6367             this.fireEvent("row" + name, this, rowIndex, e);
6368             
6369             if(cell !== false){
6370             
6371                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6372             }
6373         }
6374         
6375     },
6376     
6377     onMouseover : function(e, el)
6378     {
6379         var cell = Roo.get(el);
6380         
6381         if(!cell){
6382             return;
6383         }
6384         
6385         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6386             cell = cell.findParent('td', false, true);
6387         }
6388         
6389         var row = cell.findParent('tr', false, true);
6390         var cellIndex = cell.dom.cellIndex;
6391         var rowIndex = row.dom.rowIndex - 1; // start from 0
6392         
6393         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6394         
6395     },
6396     
6397     onMouseout : function(e, el)
6398     {
6399         var cell = Roo.get(el);
6400         
6401         if(!cell){
6402             return;
6403         }
6404         
6405         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406             cell = cell.findParent('td', false, true);
6407         }
6408         
6409         var row = cell.findParent('tr', false, true);
6410         var cellIndex = cell.dom.cellIndex;
6411         var rowIndex = row.dom.rowIndex - 1; // start from 0
6412         
6413         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6414         
6415     },
6416     
6417     onClick : function(e, el)
6418     {
6419         var cell = Roo.get(el);
6420         
6421         if(!cell || (!this.cellSelection && !this.rowSelection)){
6422             return;
6423         }
6424         
6425         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6426             cell = cell.findParent('td', false, true);
6427         }
6428         
6429         if(!cell || typeof(cell) == 'undefined'){
6430             return;
6431         }
6432         
6433         var row = cell.findParent('tr', false, true);
6434         
6435         if(!row || typeof(row) == 'undefined'){
6436             return;
6437         }
6438         
6439         var cellIndex = cell.dom.cellIndex;
6440         var rowIndex = this.getRowIndex(row);
6441         
6442         // why??? - should these not be based on SelectionModel?
6443         if(this.cellSelection){
6444             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6445         }
6446         
6447         if(this.rowSelection){
6448             this.fireEvent('rowclick', this, row, rowIndex, e);
6449         }
6450         
6451         
6452     },
6453         
6454     onDblClick : function(e,el)
6455     {
6456         var cell = Roo.get(el);
6457         
6458         if(!cell || (!this.cellSelection && !this.rowSelection)){
6459             return;
6460         }
6461         
6462         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6463             cell = cell.findParent('td', false, true);
6464         }
6465         
6466         if(!cell || typeof(cell) == 'undefined'){
6467             return;
6468         }
6469         
6470         var row = cell.findParent('tr', false, true);
6471         
6472         if(!row || typeof(row) == 'undefined'){
6473             return;
6474         }
6475         
6476         var cellIndex = cell.dom.cellIndex;
6477         var rowIndex = this.getRowIndex(row);
6478         
6479         if(this.cellSelection){
6480             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6481         }
6482         
6483         if(this.rowSelection){
6484             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6485         }
6486     },
6487     
6488     sort : function(e,el)
6489     {
6490         var col = Roo.get(el);
6491         
6492         if(!col.hasClass('sortable')){
6493             return;
6494         }
6495         
6496         var sort = col.attr('sort');
6497         var dir = 'ASC';
6498         
6499         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6500             dir = 'DESC';
6501         }
6502         
6503         this.store.sortInfo = {field : sort, direction : dir};
6504         
6505         if (this.footer) {
6506             Roo.log("calling footer first");
6507             this.footer.onClick('first');
6508         } else {
6509         
6510             this.store.load({ params : { start : 0 } });
6511         }
6512     },
6513     
6514     renderHeader : function()
6515     {
6516         var header = {
6517             tag: 'thead',
6518             cn : []
6519         };
6520         
6521         var cm = this.cm;
6522         this.totalWidth = 0;
6523         
6524         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525             
6526             var config = cm.config[i];
6527             
6528             var c = {
6529                 tag: 'th',
6530                 cls : 'x-hcol-' + i,
6531                 style : '',
6532                 html: cm.getColumnHeader(i)
6533             };
6534             
6535             var hh = '';
6536             
6537             if(typeof(config.sortable) != 'undefined' && config.sortable){
6538                 c.cls = 'sortable';
6539                 c.html = '<i class="glyphicon"></i>' + c.html;
6540             }
6541             
6542             if(typeof(config.lgHeader) != 'undefined'){
6543                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6544             }
6545             
6546             if(typeof(config.mdHeader) != 'undefined'){
6547                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6548             }
6549             
6550             if(typeof(config.smHeader) != 'undefined'){
6551                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6552             }
6553             
6554             if(typeof(config.xsHeader) != 'undefined'){
6555                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6556             }
6557             
6558             if(hh.length){
6559                 c.html = hh;
6560             }
6561             
6562             if(typeof(config.tooltip) != 'undefined'){
6563                 c.tooltip = config.tooltip;
6564             }
6565             
6566             if(typeof(config.colspan) != 'undefined'){
6567                 c.colspan = config.colspan;
6568             }
6569             
6570             if(typeof(config.hidden) != 'undefined' && config.hidden){
6571                 c.style += ' display:none;';
6572             }
6573             
6574             if(typeof(config.dataIndex) != 'undefined'){
6575                 c.sort = config.dataIndex;
6576             }
6577             
6578            
6579             
6580             if(typeof(config.align) != 'undefined' && config.align.length){
6581                 c.style += ' text-align:' + config.align + ';';
6582             }
6583             
6584             if(typeof(config.width) != 'undefined'){
6585                 c.style += ' width:' + config.width + 'px;';
6586                 this.totalWidth += config.width;
6587             } else {
6588                 this.totalWidth += 100; // assume minimum of 100 per column?
6589             }
6590             
6591             if(typeof(config.cls) != 'undefined'){
6592                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6593             }
6594             
6595             ['xs','sm','md','lg'].map(function(size){
6596                 
6597                 if(typeof(config[size]) == 'undefined'){
6598                     return;
6599                 }
6600                 
6601                 if (!config[size]) { // 0 = hidden
6602                     c.cls += ' hidden-' + size;
6603                     return;
6604                 }
6605                 
6606                 c.cls += ' col-' + size + '-' + config[size];
6607
6608             });
6609             
6610             header.cn.push(c)
6611         }
6612         
6613         return header;
6614     },
6615     
6616     renderBody : function()
6617     {
6618         var body = {
6619             tag: 'tbody',
6620             cn : [
6621                 {
6622                     tag: 'tr',
6623                     cn : [
6624                         {
6625                             tag : 'td',
6626                             colspan :  this.cm.getColumnCount()
6627                         }
6628                     ]
6629                 }
6630             ]
6631         };
6632         
6633         return body;
6634     },
6635     
6636     renderFooter : function()
6637     {
6638         var footer = {
6639             tag: 'tfoot',
6640             cn : [
6641                 {
6642                     tag: 'tr',
6643                     cn : [
6644                         {
6645                             tag : 'td',
6646                             colspan :  this.cm.getColumnCount()
6647                         }
6648                     ]
6649                 }
6650             ]
6651         };
6652         
6653         return footer;
6654     },
6655     
6656     
6657     
6658     onLoad : function()
6659     {
6660 //        Roo.log('ds onload');
6661         this.clear();
6662         
6663         var _this = this;
6664         var cm = this.cm;
6665         var ds = this.store;
6666         
6667         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6668             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6669             if (_this.store.sortInfo) {
6670                     
6671                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6672                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6673                 }
6674                 
6675                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6676                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6677                 }
6678             }
6679         });
6680         
6681         var tbody =  this.mainBody;
6682               
6683         if(ds.getCount() > 0){
6684             ds.data.each(function(d,rowIndex){
6685                 var row =  this.renderRow(cm, ds, rowIndex);
6686                 
6687                 tbody.createChild(row);
6688                 
6689                 var _this = this;
6690                 
6691                 if(row.cellObjects.length){
6692                     Roo.each(row.cellObjects, function(r){
6693                         _this.renderCellObject(r);
6694                     })
6695                 }
6696                 
6697             }, this);
6698         }
6699         
6700         var tfoot = this.el.select('tfoot', true).first();
6701         
6702         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703             
6704             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705             
6706             var total = this.ds.getTotalCount();
6707             
6708             if(this.footer.pageSize < total){
6709                 this.mainFoot.show();
6710             }
6711         }
6712         
6713         Roo.each(this.el.select('tbody td', true).elements, function(e){
6714             e.on('mouseover', _this.onMouseover, _this);
6715         });
6716         
6717         Roo.each(this.el.select('tbody td', true).elements, function(e){
6718             e.on('mouseout', _this.onMouseout, _this);
6719         });
6720         this.fireEvent('rowsrendered', this);
6721         
6722         this.autoSize();
6723     },
6724     
6725     
6726     onUpdate : function(ds,record)
6727     {
6728         this.refreshRow(record);
6729         this.autoSize();
6730     },
6731     
6732     onRemove : function(ds, record, index, isUpdate){
6733         if(isUpdate !== true){
6734             this.fireEvent("beforerowremoved", this, index, record);
6735         }
6736         var bt = this.mainBody.dom;
6737         
6738         var rows = this.el.select('tbody > tr', true).elements;
6739         
6740         if(typeof(rows[index]) != 'undefined'){
6741             bt.removeChild(rows[index].dom);
6742         }
6743         
6744 //        if(bt.rows[index]){
6745 //            bt.removeChild(bt.rows[index]);
6746 //        }
6747         
6748         if(isUpdate !== true){
6749             //this.stripeRows(index);
6750             //this.syncRowHeights(index, index);
6751             //this.layout();
6752             this.fireEvent("rowremoved", this, index, record);
6753         }
6754     },
6755     
6756     onAdd : function(ds, records, rowIndex)
6757     {
6758         //Roo.log('on Add called');
6759         // - note this does not handle multiple adding very well..
6760         var bt = this.mainBody.dom;
6761         for (var i =0 ; i < records.length;i++) {
6762             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6763             //Roo.log(records[i]);
6764             //Roo.log(this.store.getAt(rowIndex+i));
6765             this.insertRow(this.store, rowIndex + i, false);
6766             return;
6767         }
6768         
6769     },
6770     
6771     
6772     refreshRow : function(record){
6773         var ds = this.store, index;
6774         if(typeof record == 'number'){
6775             index = record;
6776             record = ds.getAt(index);
6777         }else{
6778             index = ds.indexOf(record);
6779         }
6780         this.insertRow(ds, index, true);
6781         this.autoSize();
6782         this.onRemove(ds, record, index+1, true);
6783         this.autoSize();
6784         //this.syncRowHeights(index, index);
6785         //this.layout();
6786         this.fireEvent("rowupdated", this, index, record);
6787     },
6788     
6789     insertRow : function(dm, rowIndex, isUpdate){
6790         
6791         if(!isUpdate){
6792             this.fireEvent("beforerowsinserted", this, rowIndex);
6793         }
6794             //var s = this.getScrollState();
6795         var row = this.renderRow(this.cm, this.store, rowIndex);
6796         // insert before rowIndex..
6797         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6798         
6799         var _this = this;
6800                 
6801         if(row.cellObjects.length){
6802             Roo.each(row.cellObjects, function(r){
6803                 _this.renderCellObject(r);
6804             })
6805         }
6806             
6807         if(!isUpdate){
6808             this.fireEvent("rowsinserted", this, rowIndex);
6809             //this.syncRowHeights(firstRow, lastRow);
6810             //this.stripeRows(firstRow);
6811             //this.layout();
6812         }
6813         
6814     },
6815     
6816     
6817     getRowDom : function(rowIndex)
6818     {
6819         var rows = this.el.select('tbody > tr', true).elements;
6820         
6821         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6822         
6823     },
6824     // returns the object tree for a tr..
6825   
6826     
6827     renderRow : function(cm, ds, rowIndex) 
6828     {
6829         var d = ds.getAt(rowIndex);
6830         
6831         var row = {
6832             tag : 'tr',
6833             cls : 'x-row-' + rowIndex,
6834             cn : []
6835         };
6836             
6837         var cellObjects = [];
6838         
6839         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6840             var config = cm.config[i];
6841             
6842             var renderer = cm.getRenderer(i);
6843             var value = '';
6844             var id = false;
6845             
6846             if(typeof(renderer) !== 'undefined'){
6847                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848             }
6849             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6850             // and are rendered into the cells after the row is rendered - using the id for the element.
6851             
6852             if(typeof(value) === 'object'){
6853                 id = Roo.id();
6854                 cellObjects.push({
6855                     container : id,
6856                     cfg : value 
6857                 })
6858             }
6859             
6860             var rowcfg = {
6861                 record: d,
6862                 rowIndex : rowIndex,
6863                 colIndex : i,
6864                 rowClass : ''
6865             };
6866
6867             this.fireEvent('rowclass', this, rowcfg);
6868             
6869             var td = {
6870                 tag: 'td',
6871                 cls : rowcfg.rowClass + ' x-col-' + i,
6872                 style: '',
6873                 html: (typeof(value) === 'object') ? '' : value
6874             };
6875             
6876             if (id) {
6877                 td.id = id;
6878             }
6879             
6880             if(typeof(config.colspan) != 'undefined'){
6881                 td.colspan = config.colspan;
6882             }
6883             
6884             if(typeof(config.hidden) != 'undefined' && config.hidden){
6885                 td.style += ' display:none;';
6886             }
6887             
6888             if(typeof(config.align) != 'undefined' && config.align.length){
6889                 td.style += ' text-align:' + config.align + ';';
6890             }
6891             if(typeof(config.valign) != 'undefined' && config.valign.length){
6892                 td.style += ' vertical-align:' + config.valign + ';';
6893             }
6894             
6895             if(typeof(config.width) != 'undefined'){
6896                 td.style += ' width:' +  config.width + 'px;';
6897             }
6898             
6899             if(typeof(config.cursor) != 'undefined'){
6900                 td.style += ' cursor:' +  config.cursor + ';';
6901             }
6902             
6903             if(typeof(config.cls) != 'undefined'){
6904                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6905             }
6906             
6907             ['xs','sm','md','lg'].map(function(size){
6908                 
6909                 if(typeof(config[size]) == 'undefined'){
6910                     return;
6911                 }
6912                 
6913                 if (!config[size]) { // 0 = hidden
6914                     td.cls += ' hidden-' + size;
6915                     return;
6916                 }
6917                 
6918                 td.cls += ' col-' + size + '-' + config[size];
6919
6920             });
6921             
6922             row.cn.push(td);
6923            
6924         }
6925         
6926         row.cellObjects = cellObjects;
6927         
6928         return row;
6929           
6930     },
6931     
6932     
6933     
6934     onBeforeLoad : function()
6935     {
6936         
6937     },
6938      /**
6939      * Remove all rows
6940      */
6941     clear : function()
6942     {
6943         this.el.select('tbody', true).first().dom.innerHTML = '';
6944     },
6945     /**
6946      * Show or hide a row.
6947      * @param {Number} rowIndex to show or hide
6948      * @param {Boolean} state hide
6949      */
6950     setRowVisibility : function(rowIndex, state)
6951     {
6952         var bt = this.mainBody.dom;
6953         
6954         var rows = this.el.select('tbody > tr', true).elements;
6955         
6956         if(typeof(rows[rowIndex]) == 'undefined'){
6957             return;
6958         }
6959         rows[rowIndex].dom.style.display = state ? '' : 'none';
6960     },
6961     
6962     
6963     getSelectionModel : function(){
6964         if(!this.selModel){
6965             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966         }
6967         return this.selModel;
6968     },
6969     /*
6970      * Render the Roo.bootstrap object from renderder
6971      */
6972     renderCellObject : function(r)
6973     {
6974         var _this = this;
6975         
6976         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977         
6978         var t = r.cfg.render(r.container);
6979         
6980         if(r.cfg.cn){
6981             Roo.each(r.cfg.cn, function(c){
6982                 var child = {
6983                     container: t.getChildContainer(),
6984                     cfg: c
6985                 };
6986                 _this.renderCellObject(child);
6987             })
6988         }
6989     },
6990     
6991     getRowIndex : function(row)
6992     {
6993         var rowIndex = -1;
6994         
6995         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6996             if(el != row){
6997                 return;
6998             }
6999             
7000             rowIndex = index;
7001         });
7002         
7003         return rowIndex;
7004     },
7005      /**
7006      * Returns the grid's underlying element = used by panel.Grid
7007      * @return {Element} The element
7008      */
7009     getGridEl : function(){
7010         return this.el;
7011     },
7012      /**
7013      * Forces a resize - used by panel.Grid
7014      * @return {Element} The element
7015      */
7016     autoSize : function()
7017     {
7018         //var ctr = Roo.get(this.container.dom.parentElement);
7019         var ctr = Roo.get(this.el.dom);
7020         
7021         var thd = this.getGridEl().select('thead',true).first();
7022         var tbd = this.getGridEl().select('tbody', true).first();
7023         var tfd = this.getGridEl().select('tfoot', true).first();
7024         
7025         var cw = ctr.getWidth();
7026         
7027         if (tbd) {
7028             
7029             tbd.setSize(ctr.getWidth(),
7030                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031             );
7032             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7033             cw -= barsize;
7034         }
7035         cw = Math.max(cw, this.totalWidth);
7036         this.getGridEl().select('tr',true).setWidth(cw);
7037         // resize 'expandable coloumn?
7038         
7039         return; // we doe not have a view in this design..
7040         
7041     },
7042     onBodyScroll: function()
7043     {
7044         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045         if(this.mainHead){
7046             this.mainHead.setStyle({
7047                 'position' : 'relative',
7048                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7049             });
7050         }
7051         
7052         if(this.lazyLoad){
7053             
7054             var scrollHeight = this.mainBody.dom.scrollHeight;
7055             
7056             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057             
7058             var height = this.mainBody.getHeight();
7059             
7060             if(scrollHeight - height == scrollTop) {
7061                 
7062                 var total = this.ds.getTotalCount();
7063                 
7064                 if(this.footer.cursor + this.footer.pageSize < total){
7065                     
7066                     this.footer.ds.load({
7067                         params : {
7068                             start : this.footer.cursor + this.footer.pageSize,
7069                             limit : this.footer.pageSize
7070                         },
7071                         add : true
7072                     });
7073                 }
7074             }
7075             
7076         }
7077     },
7078     
7079     onHeaderChange : function()
7080     {
7081         var header = this.renderHeader();
7082         var table = this.el.select('table', true).first();
7083         
7084         this.mainHead.remove();
7085         this.mainHead = table.createChild(header, this.mainBody, false);
7086     },
7087     
7088     onHiddenChange : function(colModel, colIndex, hidden)
7089     {
7090         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7091         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092         
7093         this.CSS.updateRule(thSelector, "display", "");
7094         this.CSS.updateRule(tdSelector, "display", "");
7095         
7096         if(hidden){
7097             this.CSS.updateRule(thSelector, "display", "none");
7098             this.CSS.updateRule(tdSelector, "display", "none");
7099         }
7100         
7101         this.onHeaderChange();
7102         this.onLoad();
7103     },
7104     
7105     setColumnWidth: function(col_index, width)
7106     {
7107         // width = "md-2 xs-2..."
7108         if(!this.colModel.config[col_index]) {
7109             return;
7110         }
7111         
7112         var w = width.split(" ");
7113         
7114         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7115         
7116         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7117         
7118         
7119         for(var j = 0; j < w.length; j++) {
7120             
7121             if(!w[j]) {
7122                 continue;
7123             }
7124             
7125             var size_cls = w[j].split("-");
7126             
7127             if(!Number.isInteger(size_cls[1] * 1)) {
7128                 continue;
7129             }
7130             
7131             if(!this.colModel.config[col_index][size_cls[0]]) {
7132                 continue;
7133             }
7134             
7135             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7136                 continue;
7137             }
7138             
7139             h_row[0].classList.replace(
7140                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7141                 "col-"+size_cls[0]+"-"+size_cls[1]
7142             );
7143             
7144             for(var i = 0; i < rows.length; i++) {
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                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7157                     continue;
7158                 }
7159                 
7160                 rows[i].classList.replace(
7161                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7162                     "col-"+size_cls[0]+"-"+size_cls[1]
7163                 );
7164             }
7165             
7166             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7167         }
7168     }
7169 });
7170
7171  
7172
7173  /*
7174  * - LGPL
7175  *
7176  * table cell
7177  * 
7178  */
7179
7180 /**
7181  * @class Roo.bootstrap.TableCell
7182  * @extends Roo.bootstrap.Component
7183  * Bootstrap TableCell class
7184  * @cfg {String} html cell contain text
7185  * @cfg {String} cls cell class
7186  * @cfg {String} tag cell tag (td|th) default td
7187  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7188  * @cfg {String} align Aligns the content in a cell
7189  * @cfg {String} axis Categorizes cells
7190  * @cfg {String} bgcolor Specifies the background color of a cell
7191  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7192  * @cfg {Number} colspan Specifies the number of columns a cell should span
7193  * @cfg {String} headers Specifies one or more header cells a cell is related to
7194  * @cfg {Number} height Sets the height of a cell
7195  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7196  * @cfg {Number} rowspan Sets the number of rows a cell should span
7197  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7198  * @cfg {String} valign Vertical aligns the content in a cell
7199  * @cfg {Number} width Specifies the width of a cell
7200  * 
7201  * @constructor
7202  * Create a new TableCell
7203  * @param {Object} config The config object
7204  */
7205
7206 Roo.bootstrap.TableCell = function(config){
7207     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7208 };
7209
7210 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7211     
7212     html: false,
7213     cls: false,
7214     tag: false,
7215     abbr: false,
7216     align: false,
7217     axis: false,
7218     bgcolor: false,
7219     charoff: false,
7220     colspan: false,
7221     headers: false,
7222     height: false,
7223     nowrap: false,
7224     rowspan: false,
7225     scope: false,
7226     valign: false,
7227     width: false,
7228     
7229     
7230     getAutoCreate : function(){
7231         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7232         
7233         cfg = {
7234             tag: 'td'
7235         };
7236         
7237         if(this.tag){
7238             cfg.tag = this.tag;
7239         }
7240         
7241         if (this.html) {
7242             cfg.html=this.html
7243         }
7244         if (this.cls) {
7245             cfg.cls=this.cls
7246         }
7247         if (this.abbr) {
7248             cfg.abbr=this.abbr
7249         }
7250         if (this.align) {
7251             cfg.align=this.align
7252         }
7253         if (this.axis) {
7254             cfg.axis=this.axis
7255         }
7256         if (this.bgcolor) {
7257             cfg.bgcolor=this.bgcolor
7258         }
7259         if (this.charoff) {
7260             cfg.charoff=this.charoff
7261         }
7262         if (this.colspan) {
7263             cfg.colspan=this.colspan
7264         }
7265         if (this.headers) {
7266             cfg.headers=this.headers
7267         }
7268         if (this.height) {
7269             cfg.height=this.height
7270         }
7271         if (this.nowrap) {
7272             cfg.nowrap=this.nowrap
7273         }
7274         if (this.rowspan) {
7275             cfg.rowspan=this.rowspan
7276         }
7277         if (this.scope) {
7278             cfg.scope=this.scope
7279         }
7280         if (this.valign) {
7281             cfg.valign=this.valign
7282         }
7283         if (this.width) {
7284             cfg.width=this.width
7285         }
7286         
7287         
7288         return cfg;
7289     }
7290    
7291 });
7292
7293  
7294
7295  /*
7296  * - LGPL
7297  *
7298  * table row
7299  * 
7300  */
7301
7302 /**
7303  * @class Roo.bootstrap.TableRow
7304  * @extends Roo.bootstrap.Component
7305  * Bootstrap TableRow class
7306  * @cfg {String} cls row class
7307  * @cfg {String} align Aligns the content in a table row
7308  * @cfg {String} bgcolor Specifies a background color for a table row
7309  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7310  * @cfg {String} valign Vertical aligns the content in a table row
7311  * 
7312  * @constructor
7313  * Create a new TableRow
7314  * @param {Object} config The config object
7315  */
7316
7317 Roo.bootstrap.TableRow = function(config){
7318     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7319 };
7320
7321 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7322     
7323     cls: false,
7324     align: false,
7325     bgcolor: false,
7326     charoff: false,
7327     valign: false,
7328     
7329     getAutoCreate : function(){
7330         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7331         
7332         cfg = {
7333             tag: 'tr'
7334         };
7335             
7336         if(this.cls){
7337             cfg.cls = this.cls;
7338         }
7339         if(this.align){
7340             cfg.align = this.align;
7341         }
7342         if(this.bgcolor){
7343             cfg.bgcolor = this.bgcolor;
7344         }
7345         if(this.charoff){
7346             cfg.charoff = this.charoff;
7347         }
7348         if(this.valign){
7349             cfg.valign = this.valign;
7350         }
7351         
7352         return cfg;
7353     }
7354    
7355 });
7356
7357  
7358
7359  /*
7360  * - LGPL
7361  *
7362  * table body
7363  * 
7364  */
7365
7366 /**
7367  * @class Roo.bootstrap.TableBody
7368  * @extends Roo.bootstrap.Component
7369  * Bootstrap TableBody class
7370  * @cfg {String} cls element class
7371  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7372  * @cfg {String} align Aligns the content inside the element
7373  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7374  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7375  * 
7376  * @constructor
7377  * Create a new TableBody
7378  * @param {Object} config The config object
7379  */
7380
7381 Roo.bootstrap.TableBody = function(config){
7382     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7383 };
7384
7385 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7386     
7387     cls: false,
7388     tag: false,
7389     align: false,
7390     charoff: false,
7391     valign: false,
7392     
7393     getAutoCreate : function(){
7394         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7395         
7396         cfg = {
7397             tag: 'tbody'
7398         };
7399             
7400         if (this.cls) {
7401             cfg.cls=this.cls
7402         }
7403         if(this.tag){
7404             cfg.tag = this.tag;
7405         }
7406         
7407         if(this.align){
7408             cfg.align = this.align;
7409         }
7410         if(this.charoff){
7411             cfg.charoff = this.charoff;
7412         }
7413         if(this.valign){
7414             cfg.valign = this.valign;
7415         }
7416         
7417         return cfg;
7418     }
7419     
7420     
7421 //    initEvents : function()
7422 //    {
7423 //        
7424 //        if(!this.store){
7425 //            return;
7426 //        }
7427 //        
7428 //        this.store = Roo.factory(this.store, Roo.data);
7429 //        this.store.on('load', this.onLoad, this);
7430 //        
7431 //        this.store.load();
7432 //        
7433 //    },
7434 //    
7435 //    onLoad: function () 
7436 //    {   
7437 //        this.fireEvent('load', this);
7438 //    }
7439 //    
7440 //   
7441 });
7442
7443  
7444
7445  /*
7446  * Based on:
7447  * Ext JS Library 1.1.1
7448  * Copyright(c) 2006-2007, Ext JS, LLC.
7449  *
7450  * Originally Released Under LGPL - original licence link has changed is not relivant.
7451  *
7452  * Fork - LGPL
7453  * <script type="text/javascript">
7454  */
7455
7456 // as we use this in bootstrap.
7457 Roo.namespace('Roo.form');
7458  /**
7459  * @class Roo.form.Action
7460  * Internal Class used to handle form actions
7461  * @constructor
7462  * @param {Roo.form.BasicForm} el The form element or its id
7463  * @param {Object} config Configuration options
7464  */
7465
7466  
7467  
7468 // define the action interface
7469 Roo.form.Action = function(form, options){
7470     this.form = form;
7471     this.options = options || {};
7472 };
7473 /**
7474  * Client Validation Failed
7475  * @const 
7476  */
7477 Roo.form.Action.CLIENT_INVALID = 'client';
7478 /**
7479  * Server Validation Failed
7480  * @const 
7481  */
7482 Roo.form.Action.SERVER_INVALID = 'server';
7483  /**
7484  * Connect to Server Failed
7485  * @const 
7486  */
7487 Roo.form.Action.CONNECT_FAILURE = 'connect';
7488 /**
7489  * Reading Data from Server Failed
7490  * @const 
7491  */
7492 Roo.form.Action.LOAD_FAILURE = 'load';
7493
7494 Roo.form.Action.prototype = {
7495     type : 'default',
7496     failureType : undefined,
7497     response : undefined,
7498     result : undefined,
7499
7500     // interface method
7501     run : function(options){
7502
7503     },
7504
7505     // interface method
7506     success : function(response){
7507
7508     },
7509
7510     // interface method
7511     handleResponse : function(response){
7512
7513     },
7514
7515     // default connection failure
7516     failure : function(response){
7517         
7518         this.response = response;
7519         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7520         this.form.afterAction(this, false);
7521     },
7522
7523     processResponse : function(response){
7524         this.response = response;
7525         if(!response.responseText){
7526             return true;
7527         }
7528         this.result = this.handleResponse(response);
7529         return this.result;
7530     },
7531
7532     // utility functions used internally
7533     getUrl : function(appendParams){
7534         var url = this.options.url || this.form.url || this.form.el.dom.action;
7535         if(appendParams){
7536             var p = this.getParams();
7537             if(p){
7538                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7539             }
7540         }
7541         return url;
7542     },
7543
7544     getMethod : function(){
7545         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7546     },
7547
7548     getParams : function(){
7549         var bp = this.form.baseParams;
7550         var p = this.options.params;
7551         if(p){
7552             if(typeof p == "object"){
7553                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7554             }else if(typeof p == 'string' && bp){
7555                 p += '&' + Roo.urlEncode(bp);
7556             }
7557         }else if(bp){
7558             p = Roo.urlEncode(bp);
7559         }
7560         return p;
7561     },
7562
7563     createCallback : function(){
7564         return {
7565             success: this.success,
7566             failure: this.failure,
7567             scope: this,
7568             timeout: (this.form.timeout*1000),
7569             upload: this.form.fileUpload ? this.success : undefined
7570         };
7571     }
7572 };
7573
7574 Roo.form.Action.Submit = function(form, options){
7575     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7576 };
7577
7578 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7579     type : 'submit',
7580
7581     haveProgress : false,
7582     uploadComplete : false,
7583     
7584     // uploadProgress indicator.
7585     uploadProgress : function()
7586     {
7587         if (!this.form.progressUrl) {
7588             return;
7589         }
7590         
7591         if (!this.haveProgress) {
7592             Roo.MessageBox.progress("Uploading", "Uploading");
7593         }
7594         if (this.uploadComplete) {
7595            Roo.MessageBox.hide();
7596            return;
7597         }
7598         
7599         this.haveProgress = true;
7600    
7601         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7602         
7603         var c = new Roo.data.Connection();
7604         c.request({
7605             url : this.form.progressUrl,
7606             params: {
7607                 id : uid
7608             },
7609             method: 'GET',
7610             success : function(req){
7611                //console.log(data);
7612                 var rdata = false;
7613                 var edata;
7614                 try  {
7615                    rdata = Roo.decode(req.responseText)
7616                 } catch (e) {
7617                     Roo.log("Invalid data from server..");
7618                     Roo.log(edata);
7619                     return;
7620                 }
7621                 if (!rdata || !rdata.success) {
7622                     Roo.log(rdata);
7623                     Roo.MessageBox.alert(Roo.encode(rdata));
7624                     return;
7625                 }
7626                 var data = rdata.data;
7627                 
7628                 if (this.uploadComplete) {
7629                    Roo.MessageBox.hide();
7630                    return;
7631                 }
7632                    
7633                 if (data){
7634                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7635                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7636                     );
7637                 }
7638                 this.uploadProgress.defer(2000,this);
7639             },
7640        
7641             failure: function(data) {
7642                 Roo.log('progress url failed ');
7643                 Roo.log(data);
7644             },
7645             scope : this
7646         });
7647            
7648     },
7649     
7650     
7651     run : function()
7652     {
7653         // run get Values on the form, so it syncs any secondary forms.
7654         this.form.getValues();
7655         
7656         var o = this.options;
7657         var method = this.getMethod();
7658         var isPost = method == 'POST';
7659         if(o.clientValidation === false || this.form.isValid()){
7660             
7661             if (this.form.progressUrl) {
7662                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7663                     (new Date() * 1) + '' + Math.random());
7664                     
7665             } 
7666             
7667             
7668             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7669                 form:this.form.el.dom,
7670                 url:this.getUrl(!isPost),
7671                 method: method,
7672                 params:isPost ? this.getParams() : null,
7673                 isUpload: this.form.fileUpload
7674             }));
7675             
7676             this.uploadProgress();
7677
7678         }else if (o.clientValidation !== false){ // client validation failed
7679             this.failureType = Roo.form.Action.CLIENT_INVALID;
7680             this.form.afterAction(this, false);
7681         }
7682     },
7683
7684     success : function(response)
7685     {
7686         this.uploadComplete= true;
7687         if (this.haveProgress) {
7688             Roo.MessageBox.hide();
7689         }
7690         
7691         
7692         var result = this.processResponse(response);
7693         if(result === true || result.success){
7694             this.form.afterAction(this, true);
7695             return;
7696         }
7697         if(result.errors){
7698             this.form.markInvalid(result.errors);
7699             this.failureType = Roo.form.Action.SERVER_INVALID;
7700         }
7701         this.form.afterAction(this, false);
7702     },
7703     failure : function(response)
7704     {
7705         this.uploadComplete= true;
7706         if (this.haveProgress) {
7707             Roo.MessageBox.hide();
7708         }
7709         
7710         this.response = response;
7711         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7712         this.form.afterAction(this, false);
7713     },
7714     
7715     handleResponse : function(response){
7716         if(this.form.errorReader){
7717             var rs = this.form.errorReader.read(response);
7718             var errors = [];
7719             if(rs.records){
7720                 for(var i = 0, len = rs.records.length; i < len; i++) {
7721                     var r = rs.records[i];
7722                     errors[i] = r.data;
7723                 }
7724             }
7725             if(errors.length < 1){
7726                 errors = null;
7727             }
7728             return {
7729                 success : rs.success,
7730                 errors : errors
7731             };
7732         }
7733         var ret = false;
7734         try {
7735             ret = Roo.decode(response.responseText);
7736         } catch (e) {
7737             ret = {
7738                 success: false,
7739                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7740                 errors : []
7741             };
7742         }
7743         return ret;
7744         
7745     }
7746 });
7747
7748
7749 Roo.form.Action.Load = function(form, options){
7750     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7751     this.reader = this.form.reader;
7752 };
7753
7754 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7755     type : 'load',
7756
7757     run : function(){
7758         
7759         Roo.Ajax.request(Roo.apply(
7760                 this.createCallback(), {
7761                     method:this.getMethod(),
7762                     url:this.getUrl(false),
7763                     params:this.getParams()
7764         }));
7765     },
7766
7767     success : function(response){
7768         
7769         var result = this.processResponse(response);
7770         if(result === true || !result.success || !result.data){
7771             this.failureType = Roo.form.Action.LOAD_FAILURE;
7772             this.form.afterAction(this, false);
7773             return;
7774         }
7775         this.form.clearInvalid();
7776         this.form.setValues(result.data);
7777         this.form.afterAction(this, true);
7778     },
7779
7780     handleResponse : function(response){
7781         if(this.form.reader){
7782             var rs = this.form.reader.read(response);
7783             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7784             return {
7785                 success : rs.success,
7786                 data : data
7787             };
7788         }
7789         return Roo.decode(response.responseText);
7790     }
7791 });
7792
7793 Roo.form.Action.ACTION_TYPES = {
7794     'load' : Roo.form.Action.Load,
7795     'submit' : Roo.form.Action.Submit
7796 };/*
7797  * - LGPL
7798  *
7799  * form
7800  *
7801  */
7802
7803 /**
7804  * @class Roo.bootstrap.Form
7805  * @extends Roo.bootstrap.Component
7806  * Bootstrap Form class
7807  * @cfg {String} method  GET | POST (default POST)
7808  * @cfg {String} labelAlign top | left (default top)
7809  * @cfg {String} align left  | right - for navbars
7810  * @cfg {Boolean} loadMask load mask when submit (default true)
7811
7812  *
7813  * @constructor
7814  * Create a new Form
7815  * @param {Object} config The config object
7816  */
7817
7818
7819 Roo.bootstrap.Form = function(config){
7820     
7821     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7822     
7823     Roo.bootstrap.Form.popover.apply();
7824     
7825     this.addEvents({
7826         /**
7827          * @event clientvalidation
7828          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7829          * @param {Form} this
7830          * @param {Boolean} valid true if the form has passed client-side validation
7831          */
7832         clientvalidation: true,
7833         /**
7834          * @event beforeaction
7835          * Fires before any action is performed. Return false to cancel the action.
7836          * @param {Form} this
7837          * @param {Action} action The action to be performed
7838          */
7839         beforeaction: true,
7840         /**
7841          * @event actionfailed
7842          * Fires when an action fails.
7843          * @param {Form} this
7844          * @param {Action} action The action that failed
7845          */
7846         actionfailed : true,
7847         /**
7848          * @event actioncomplete
7849          * Fires when an action is completed.
7850          * @param {Form} this
7851          * @param {Action} action The action that completed
7852          */
7853         actioncomplete : true
7854     });
7855 };
7856
7857 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7858
7859      /**
7860      * @cfg {String} method
7861      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7862      */
7863     method : 'POST',
7864     /**
7865      * @cfg {String} url
7866      * The URL to use for form actions if one isn't supplied in the action options.
7867      */
7868     /**
7869      * @cfg {Boolean} fileUpload
7870      * Set to true if this form is a file upload.
7871      */
7872
7873     /**
7874      * @cfg {Object} baseParams
7875      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7876      */
7877
7878     /**
7879      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7880      */
7881     timeout: 30,
7882     /**
7883      * @cfg {Sting} align (left|right) for navbar forms
7884      */
7885     align : 'left',
7886
7887     // private
7888     activeAction : null,
7889
7890     /**
7891      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7892      * element by passing it or its id or mask the form itself by passing in true.
7893      * @type Mixed
7894      */
7895     waitMsgTarget : false,
7896
7897     loadMask : true,
7898     
7899     /**
7900      * @cfg {Boolean} errorMask (true|false) default false
7901      */
7902     errorMask : false,
7903     
7904     /**
7905      * @cfg {Number} maskOffset Default 100
7906      */
7907     maskOffset : 100,
7908     
7909     /**
7910      * @cfg {Boolean} maskBody
7911      */
7912     maskBody : false,
7913
7914     getAutoCreate : function(){
7915
7916         var cfg = {
7917             tag: 'form',
7918             method : this.method || 'POST',
7919             id : this.id || Roo.id(),
7920             cls : ''
7921         };
7922         if (this.parent().xtype.match(/^Nav/)) {
7923             cfg.cls = 'navbar-form navbar-' + this.align;
7924
7925         }
7926
7927         if (this.labelAlign == 'left' ) {
7928             cfg.cls += ' form-horizontal';
7929         }
7930
7931
7932         return cfg;
7933     },
7934     initEvents : function()
7935     {
7936         this.el.on('submit', this.onSubmit, this);
7937         // this was added as random key presses on the form where triggering form submit.
7938         this.el.on('keypress', function(e) {
7939             if (e.getCharCode() != 13) {
7940                 return true;
7941             }
7942             // we might need to allow it for textareas.. and some other items.
7943             // check e.getTarget().
7944
7945             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7946                 return true;
7947             }
7948
7949             Roo.log("keypress blocked");
7950
7951             e.preventDefault();
7952             return false;
7953         });
7954         
7955     },
7956     // private
7957     onSubmit : function(e){
7958         e.stopEvent();
7959     },
7960
7961      /**
7962      * Returns true if client-side validation on the form is successful.
7963      * @return Boolean
7964      */
7965     isValid : function(){
7966         var items = this.getItems();
7967         var valid = true;
7968         var target = false;
7969         
7970         items.each(function(f){
7971             
7972             if(f.validate()){
7973                 return;
7974             }
7975             
7976             Roo.log('invalid field: ' + f.name);
7977             
7978             valid = false;
7979
7980             if(!target && f.el.isVisible(true)){
7981                 target = f;
7982             }
7983            
7984         });
7985         
7986         if(this.errorMask && !valid){
7987             Roo.bootstrap.Form.popover.mask(this, target);
7988         }
7989         
7990         return valid;
7991     },
7992     
7993     /**
7994      * Returns true if any fields in this form have changed since their original load.
7995      * @return Boolean
7996      */
7997     isDirty : function(){
7998         var dirty = false;
7999         var items = this.getItems();
8000         items.each(function(f){
8001            if(f.isDirty()){
8002                dirty = true;
8003                return false;
8004            }
8005            return true;
8006         });
8007         return dirty;
8008     },
8009      /**
8010      * Performs a predefined action (submit or load) or custom actions you define on this form.
8011      * @param {String} actionName The name of the action type
8012      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8013      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8014      * accept other config options):
8015      * <pre>
8016 Property          Type             Description
8017 ----------------  ---------------  ----------------------------------------------------------------------------------
8018 url               String           The url for the action (defaults to the form's url)
8019 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8020 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8021 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8022                                    validate the form on the client (defaults to false)
8023      * </pre>
8024      * @return {BasicForm} this
8025      */
8026     doAction : function(action, options){
8027         if(typeof action == 'string'){
8028             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8029         }
8030         if(this.fireEvent('beforeaction', this, action) !== false){
8031             this.beforeAction(action);
8032             action.run.defer(100, action);
8033         }
8034         return this;
8035     },
8036
8037     // private
8038     beforeAction : function(action){
8039         var o = action.options;
8040         
8041         if(this.loadMask){
8042             
8043             if(this.maskBody){
8044                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8045             } else {
8046                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8047             }
8048         }
8049         // not really supported yet.. ??
8050
8051         //if(this.waitMsgTarget === true){
8052         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8053         //}else if(this.waitMsgTarget){
8054         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8055         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8056         //}else {
8057         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8058        // }
8059
8060     },
8061
8062     // private
8063     afterAction : function(action, success){
8064         this.activeAction = null;
8065         var o = action.options;
8066
8067         if(this.loadMask){
8068             
8069             if(this.maskBody){
8070                 Roo.get(document.body).unmask();
8071             } else {
8072                 this.el.unmask();
8073             }
8074         }
8075         
8076         //if(this.waitMsgTarget === true){
8077 //            this.el.unmask();
8078         //}else if(this.waitMsgTarget){
8079         //    this.waitMsgTarget.unmask();
8080         //}else{
8081         //    Roo.MessageBox.updateProgress(1);
8082         //    Roo.MessageBox.hide();
8083        // }
8084         //
8085         if(success){
8086             if(o.reset){
8087                 this.reset();
8088             }
8089             Roo.callback(o.success, o.scope, [this, action]);
8090             this.fireEvent('actioncomplete', this, action);
8091
8092         }else{
8093
8094             // failure condition..
8095             // we have a scenario where updates need confirming.
8096             // eg. if a locking scenario exists..
8097             // we look for { errors : { needs_confirm : true }} in the response.
8098             if (
8099                 (typeof(action.result) != 'undefined')  &&
8100                 (typeof(action.result.errors) != 'undefined')  &&
8101                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8102            ){
8103                 var _t = this;
8104                 Roo.log("not supported yet");
8105                  /*
8106
8107                 Roo.MessageBox.confirm(
8108                     "Change requires confirmation",
8109                     action.result.errorMsg,
8110                     function(r) {
8111                         if (r != 'yes') {
8112                             return;
8113                         }
8114                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8115                     }
8116
8117                 );
8118                 */
8119
8120
8121                 return;
8122             }
8123
8124             Roo.callback(o.failure, o.scope, [this, action]);
8125             // show an error message if no failed handler is set..
8126             if (!this.hasListener('actionfailed')) {
8127                 Roo.log("need to add dialog support");
8128                 /*
8129                 Roo.MessageBox.alert("Error",
8130                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8131                         action.result.errorMsg :
8132                         "Saving Failed, please check your entries or try again"
8133                 );
8134                 */
8135             }
8136
8137             this.fireEvent('actionfailed', this, action);
8138         }
8139
8140     },
8141     /**
8142      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8143      * @param {String} id The value to search for
8144      * @return Field
8145      */
8146     findField : function(id){
8147         var items = this.getItems();
8148         var field = items.get(id);
8149         if(!field){
8150              items.each(function(f){
8151                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8152                     field = f;
8153                     return false;
8154                 }
8155                 return true;
8156             });
8157         }
8158         return field || null;
8159     },
8160      /**
8161      * Mark fields in this form invalid in bulk.
8162      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8163      * @return {BasicForm} this
8164      */
8165     markInvalid : function(errors){
8166         if(errors instanceof Array){
8167             for(var i = 0, len = errors.length; i < len; i++){
8168                 var fieldError = errors[i];
8169                 var f = this.findField(fieldError.id);
8170                 if(f){
8171                     f.markInvalid(fieldError.msg);
8172                 }
8173             }
8174         }else{
8175             var field, id;
8176             for(id in errors){
8177                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8178                     field.markInvalid(errors[id]);
8179                 }
8180             }
8181         }
8182         //Roo.each(this.childForms || [], function (f) {
8183         //    f.markInvalid(errors);
8184         //});
8185
8186         return this;
8187     },
8188
8189     /**
8190      * Set values for fields in this form in bulk.
8191      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8192      * @return {BasicForm} this
8193      */
8194     setValues : function(values){
8195         if(values instanceof Array){ // array of objects
8196             for(var i = 0, len = values.length; i < len; i++){
8197                 var v = values[i];
8198                 var f = this.findField(v.id);
8199                 if(f){
8200                     f.setValue(v.value);
8201                     if(this.trackResetOnLoad){
8202                         f.originalValue = f.getValue();
8203                     }
8204                 }
8205             }
8206         }else{ // object hash
8207             var field, id;
8208             for(id in values){
8209                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8210
8211                     if (field.setFromData &&
8212                         field.valueField &&
8213                         field.displayField &&
8214                         // combos' with local stores can
8215                         // be queried via setValue()
8216                         // to set their value..
8217                         (field.store && !field.store.isLocal)
8218                         ) {
8219                         // it's a combo
8220                         var sd = { };
8221                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8222                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8223                         field.setFromData(sd);
8224
8225                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8226                         
8227                         field.setFromData(values);
8228                         
8229                     } else {
8230                         field.setValue(values[id]);
8231                     }
8232
8233
8234                     if(this.trackResetOnLoad){
8235                         field.originalValue = field.getValue();
8236                     }
8237                 }
8238             }
8239         }
8240
8241         //Roo.each(this.childForms || [], function (f) {
8242         //    f.setValues(values);
8243         //});
8244
8245         return this;
8246     },
8247
8248     /**
8249      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8250      * they are returned as an array.
8251      * @param {Boolean} asString
8252      * @return {Object}
8253      */
8254     getValues : function(asString){
8255         //if (this.childForms) {
8256             // copy values from the child forms
8257         //    Roo.each(this.childForms, function (f) {
8258         //        this.setValues(f.getValues());
8259         //    }, this);
8260         //}
8261
8262
8263
8264         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8265         if(asString === true){
8266             return fs;
8267         }
8268         return Roo.urlDecode(fs);
8269     },
8270
8271     /**
8272      * Returns the fields in this form as an object with key/value pairs.
8273      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8274      * @return {Object}
8275      */
8276     getFieldValues : function(with_hidden)
8277     {
8278         var items = this.getItems();
8279         var ret = {};
8280         items.each(function(f){
8281             
8282             if (!f.getName()) {
8283                 return;
8284             }
8285             
8286             var v = f.getValue();
8287             
8288             if (f.inputType =='radio') {
8289                 if (typeof(ret[f.getName()]) == 'undefined') {
8290                     ret[f.getName()] = ''; // empty..
8291                 }
8292
8293                 if (!f.el.dom.checked) {
8294                     return;
8295
8296                 }
8297                 v = f.el.dom.value;
8298
8299             }
8300             
8301             if(f.xtype == 'MoneyField'){
8302                 ret[f.currencyName] = f.getCurrency();
8303             }
8304
8305             // not sure if this supported any more..
8306             if ((typeof(v) == 'object') && f.getRawValue) {
8307                 v = f.getRawValue() ; // dates..
8308             }
8309             // combo boxes where name != hiddenName...
8310             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8311                 ret[f.name] = f.getRawValue();
8312             }
8313             ret[f.getName()] = v;
8314         });
8315
8316         return ret;
8317     },
8318
8319     /**
8320      * Clears all invalid messages in this form.
8321      * @return {BasicForm} this
8322      */
8323     clearInvalid : function(){
8324         var items = this.getItems();
8325
8326         items.each(function(f){
8327            f.clearInvalid();
8328         });
8329
8330         return this;
8331     },
8332
8333     /**
8334      * Resets this form.
8335      * @return {BasicForm} this
8336      */
8337     reset : function(){
8338         var items = this.getItems();
8339         items.each(function(f){
8340             f.reset();
8341         });
8342
8343         Roo.each(this.childForms || [], function (f) {
8344             f.reset();
8345         });
8346
8347
8348         return this;
8349     },
8350     
8351     getItems : function()
8352     {
8353         var r=new Roo.util.MixedCollection(false, function(o){
8354             return o.id || (o.id = Roo.id());
8355         });
8356         var iter = function(el) {
8357             if (el.inputEl) {
8358                 r.add(el);
8359             }
8360             if (!el.items) {
8361                 return;
8362             }
8363             Roo.each(el.items,function(e) {
8364                 iter(e);
8365             });
8366         };
8367
8368         iter(this);
8369         return r;
8370     },
8371     
8372     hideFields : function(items)
8373     {
8374         Roo.each(items, function(i){
8375             
8376             var f = this.findField(i);
8377             
8378             if(!f){
8379                 return;
8380             }
8381             
8382             f.hide();
8383             
8384         }, this);
8385     },
8386     
8387     showFields : function(items)
8388     {
8389         Roo.each(items, function(i){
8390             
8391             var f = this.findField(i);
8392             
8393             if(!f){
8394                 return;
8395             }
8396             
8397             f.show();
8398             
8399         }, this);
8400     }
8401
8402 });
8403
8404 Roo.apply(Roo.bootstrap.Form, {
8405     
8406     popover : {
8407         
8408         padding : 5,
8409         
8410         isApplied : false,
8411         
8412         isMasked : false,
8413         
8414         form : false,
8415         
8416         target : false,
8417         
8418         toolTip : false,
8419         
8420         intervalID : false,
8421         
8422         maskEl : false,
8423         
8424         apply : function()
8425         {
8426             if(this.isApplied){
8427                 return;
8428             }
8429             
8430             this.maskEl = {
8431                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8432                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8433                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8434                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8435             };
8436             
8437             this.maskEl.top.enableDisplayMode("block");
8438             this.maskEl.left.enableDisplayMode("block");
8439             this.maskEl.bottom.enableDisplayMode("block");
8440             this.maskEl.right.enableDisplayMode("block");
8441             
8442             this.toolTip = new Roo.bootstrap.Tooltip({
8443                 cls : 'roo-form-error-popover',
8444                 alignment : {
8445                     'left' : ['r-l', [-2,0], 'right'],
8446                     'right' : ['l-r', [2,0], 'left'],
8447                     'bottom' : ['tl-bl', [0,2], 'top'],
8448                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8449                 }
8450             });
8451             
8452             this.toolTip.render(Roo.get(document.body));
8453
8454             this.toolTip.el.enableDisplayMode("block");
8455             
8456             Roo.get(document.body).on('click', function(){
8457                 this.unmask();
8458             }, this);
8459             
8460             Roo.get(document.body).on('touchstart', function(){
8461                 this.unmask();
8462             }, this);
8463             
8464             this.isApplied = true
8465         },
8466         
8467         mask : function(form, target)
8468         {
8469             this.form = form;
8470             
8471             this.target = target;
8472             
8473             if(!this.form.errorMask || !target.el){
8474                 return;
8475             }
8476             
8477             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8478             
8479             Roo.log(scrollable);
8480             
8481             var ot = this.target.el.calcOffsetsTo(scrollable);
8482             
8483             var scrollTo = ot[1] - this.form.maskOffset;
8484             
8485             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8486             
8487             scrollable.scrollTo('top', scrollTo);
8488             
8489             var box = this.target.el.getBox();
8490             Roo.log(box);
8491             var zIndex = Roo.bootstrap.Modal.zIndex++;
8492
8493             
8494             this.maskEl.top.setStyle('position', 'absolute');
8495             this.maskEl.top.setStyle('z-index', zIndex);
8496             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8497             this.maskEl.top.setLeft(0);
8498             this.maskEl.top.setTop(0);
8499             this.maskEl.top.show();
8500             
8501             this.maskEl.left.setStyle('position', 'absolute');
8502             this.maskEl.left.setStyle('z-index', zIndex);
8503             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8504             this.maskEl.left.setLeft(0);
8505             this.maskEl.left.setTop(box.y - this.padding);
8506             this.maskEl.left.show();
8507
8508             this.maskEl.bottom.setStyle('position', 'absolute');
8509             this.maskEl.bottom.setStyle('z-index', zIndex);
8510             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8511             this.maskEl.bottom.setLeft(0);
8512             this.maskEl.bottom.setTop(box.bottom + this.padding);
8513             this.maskEl.bottom.show();
8514
8515             this.maskEl.right.setStyle('position', 'absolute');
8516             this.maskEl.right.setStyle('z-index', zIndex);
8517             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8518             this.maskEl.right.setLeft(box.right + this.padding);
8519             this.maskEl.right.setTop(box.y - this.padding);
8520             this.maskEl.right.show();
8521
8522             this.toolTip.bindEl = this.target.el;
8523
8524             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8525
8526             var tip = this.target.blankText;
8527
8528             if(this.target.getValue() !== '' ) {
8529                 
8530                 if (this.target.invalidText.length) {
8531                     tip = this.target.invalidText;
8532                 } else if (this.target.regexText.length){
8533                     tip = this.target.regexText;
8534                 }
8535             }
8536
8537             this.toolTip.show(tip);
8538
8539             this.intervalID = window.setInterval(function() {
8540                 Roo.bootstrap.Form.popover.unmask();
8541             }, 10000);
8542
8543             window.onwheel = function(){ return false;};
8544             
8545             (function(){ this.isMasked = true; }).defer(500, this);
8546             
8547         },
8548         
8549         unmask : function()
8550         {
8551             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8552                 return;
8553             }
8554             
8555             this.maskEl.top.setStyle('position', 'absolute');
8556             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8557             this.maskEl.top.hide();
8558
8559             this.maskEl.left.setStyle('position', 'absolute');
8560             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8561             this.maskEl.left.hide();
8562
8563             this.maskEl.bottom.setStyle('position', 'absolute');
8564             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8565             this.maskEl.bottom.hide();
8566
8567             this.maskEl.right.setStyle('position', 'absolute');
8568             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8569             this.maskEl.right.hide();
8570             
8571             this.toolTip.hide();
8572             
8573             this.toolTip.el.hide();
8574             
8575             window.onwheel = function(){ return true;};
8576             
8577             if(this.intervalID){
8578                 window.clearInterval(this.intervalID);
8579                 this.intervalID = false;
8580             }
8581             
8582             this.isMasked = false;
8583             
8584         }
8585         
8586     }
8587     
8588 });
8589
8590 /*
8591  * Based on:
8592  * Ext JS Library 1.1.1
8593  * Copyright(c) 2006-2007, Ext JS, LLC.
8594  *
8595  * Originally Released Under LGPL - original licence link has changed is not relivant.
8596  *
8597  * Fork - LGPL
8598  * <script type="text/javascript">
8599  */
8600 /**
8601  * @class Roo.form.VTypes
8602  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8603  * @singleton
8604  */
8605 Roo.form.VTypes = function(){
8606     // closure these in so they are only created once.
8607     var alpha = /^[a-zA-Z_]+$/;
8608     var alphanum = /^[a-zA-Z0-9_]+$/;
8609     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8610     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8611
8612     // All these messages and functions are configurable
8613     return {
8614         /**
8615          * The function used to validate email addresses
8616          * @param {String} value The email address
8617          */
8618         'email' : function(v){
8619             return email.test(v);
8620         },
8621         /**
8622          * The error text to display when the email validation function returns false
8623          * @type String
8624          */
8625         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8626         /**
8627          * The keystroke filter mask to be applied on email input
8628          * @type RegExp
8629          */
8630         'emailMask' : /[a-z0-9_\.\-@]/i,
8631
8632         /**
8633          * The function used to validate URLs
8634          * @param {String} value The URL
8635          */
8636         'url' : function(v){
8637             return url.test(v);
8638         },
8639         /**
8640          * The error text to display when the url validation function returns false
8641          * @type String
8642          */
8643         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8644         
8645         /**
8646          * The function used to validate alpha values
8647          * @param {String} value The value
8648          */
8649         'alpha' : function(v){
8650             return alpha.test(v);
8651         },
8652         /**
8653          * The error text to display when the alpha validation function returns false
8654          * @type String
8655          */
8656         'alphaText' : 'This field should only contain letters and _',
8657         /**
8658          * The keystroke filter mask to be applied on alpha input
8659          * @type RegExp
8660          */
8661         'alphaMask' : /[a-z_]/i,
8662
8663         /**
8664          * The function used to validate alphanumeric values
8665          * @param {String} value The value
8666          */
8667         'alphanum' : function(v){
8668             return alphanum.test(v);
8669         },
8670         /**
8671          * The error text to display when the alphanumeric validation function returns false
8672          * @type String
8673          */
8674         'alphanumText' : 'This field should only contain letters, numbers and _',
8675         /**
8676          * The keystroke filter mask to be applied on alphanumeric input
8677          * @type RegExp
8678          */
8679         'alphanumMask' : /[a-z0-9_]/i
8680     };
8681 }();/*
8682  * - LGPL
8683  *
8684  * Input
8685  * 
8686  */
8687
8688 /**
8689  * @class Roo.bootstrap.Input
8690  * @extends Roo.bootstrap.Component
8691  * Bootstrap Input class
8692  * @cfg {Boolean} disabled is it disabled
8693  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8694  * @cfg {String} name name of the input
8695  * @cfg {string} fieldLabel - the label associated
8696  * @cfg {string} placeholder - placeholder to put in text.
8697  * @cfg {string}  before - input group add on before
8698  * @cfg {string} after - input group add on after
8699  * @cfg {string} size - (lg|sm) or leave empty..
8700  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8701  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8702  * @cfg {Number} md colspan out of 12 for computer-sized screens
8703  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8704  * @cfg {string} value default value of the input
8705  * @cfg {Number} labelWidth set the width of label 
8706  * @cfg {Number} labellg set the width of label (1-12)
8707  * @cfg {Number} labelmd set the width of label (1-12)
8708  * @cfg {Number} labelsm set the width of label (1-12)
8709  * @cfg {Number} labelxs set the width of label (1-12)
8710  * @cfg {String} labelAlign (top|left)
8711  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8712  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8713  * @cfg {String} indicatorpos (left|right) default left
8714  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8715  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8716
8717  * @cfg {String} align (left|center|right) Default left
8718  * @cfg {Boolean} forceFeedback (true|false) Default false
8719  * 
8720  * @constructor
8721  * Create a new Input
8722  * @param {Object} config The config object
8723  */
8724
8725 Roo.bootstrap.Input = function(config){
8726     
8727     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8728     
8729     this.addEvents({
8730         /**
8731          * @event focus
8732          * Fires when this field receives input focus.
8733          * @param {Roo.form.Field} this
8734          */
8735         focus : true,
8736         /**
8737          * @event blur
8738          * Fires when this field loses input focus.
8739          * @param {Roo.form.Field} this
8740          */
8741         blur : true,
8742         /**
8743          * @event specialkey
8744          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8745          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8746          * @param {Roo.form.Field} this
8747          * @param {Roo.EventObject} e The event object
8748          */
8749         specialkey : true,
8750         /**
8751          * @event change
8752          * Fires just before the field blurs if the field value has changed.
8753          * @param {Roo.form.Field} this
8754          * @param {Mixed} newValue The new value
8755          * @param {Mixed} oldValue The original value
8756          */
8757         change : true,
8758         /**
8759          * @event invalid
8760          * Fires after the field has been marked as invalid.
8761          * @param {Roo.form.Field} this
8762          * @param {String} msg The validation message
8763          */
8764         invalid : true,
8765         /**
8766          * @event valid
8767          * Fires after the field has been validated with no errors.
8768          * @param {Roo.form.Field} this
8769          */
8770         valid : true,
8771          /**
8772          * @event keyup
8773          * Fires after the key up
8774          * @param {Roo.form.Field} this
8775          * @param {Roo.EventObject}  e The event Object
8776          */
8777         keyup : true
8778     });
8779 };
8780
8781 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8782      /**
8783      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8784       automatic validation (defaults to "keyup").
8785      */
8786     validationEvent : "keyup",
8787      /**
8788      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8789      */
8790     validateOnBlur : true,
8791     /**
8792      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8793      */
8794     validationDelay : 250,
8795      /**
8796      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8797      */
8798     focusClass : "x-form-focus",  // not needed???
8799     
8800        
8801     /**
8802      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8803      */
8804     invalidClass : "has-warning",
8805     
8806     /**
8807      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8808      */
8809     validClass : "has-success",
8810     
8811     /**
8812      * @cfg {Boolean} hasFeedback (true|false) default true
8813      */
8814     hasFeedback : true,
8815     
8816     /**
8817      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8818      */
8819     invalidFeedbackClass : "glyphicon-warning-sign",
8820     
8821     /**
8822      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8823      */
8824     validFeedbackClass : "glyphicon-ok",
8825     
8826     /**
8827      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8828      */
8829     selectOnFocus : false,
8830     
8831      /**
8832      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8833      */
8834     maskRe : null,
8835        /**
8836      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8837      */
8838     vtype : null,
8839     
8840       /**
8841      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8842      */
8843     disableKeyFilter : false,
8844     
8845        /**
8846      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8847      */
8848     disabled : false,
8849      /**
8850      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8851      */
8852     allowBlank : true,
8853     /**
8854      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8855      */
8856     blankText : "Please complete this mandatory field",
8857     
8858      /**
8859      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8860      */
8861     minLength : 0,
8862     /**
8863      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8864      */
8865     maxLength : Number.MAX_VALUE,
8866     /**
8867      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8868      */
8869     minLengthText : "The minimum length for this field is {0}",
8870     /**
8871      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8872      */
8873     maxLengthText : "The maximum length for this field is {0}",
8874   
8875     
8876     /**
8877      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8878      * If available, this function will be called only after the basic validators all return true, and will be passed the
8879      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8880      */
8881     validator : null,
8882     /**
8883      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8884      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8885      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8886      */
8887     regex : null,
8888     /**
8889      * @cfg {String} regexText -- Depricated - use Invalid Text
8890      */
8891     regexText : "",
8892     
8893     /**
8894      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8895      */
8896     invalidText : "",
8897     
8898     
8899     
8900     autocomplete: false,
8901     
8902     
8903     fieldLabel : '',
8904     inputType : 'text',
8905     
8906     name : false,
8907     placeholder: false,
8908     before : false,
8909     after : false,
8910     size : false,
8911     hasFocus : false,
8912     preventMark: false,
8913     isFormField : true,
8914     value : '',
8915     labelWidth : 2,
8916     labelAlign : false,
8917     readOnly : false,
8918     align : false,
8919     formatedValue : false,
8920     forceFeedback : false,
8921     
8922     indicatorpos : 'left',
8923     
8924     labellg : 0,
8925     labelmd : 0,
8926     labelsm : 0,
8927     labelxs : 0,
8928     
8929     capture : '',
8930     accept : '',
8931     
8932     parentLabelAlign : function()
8933     {
8934         var parent = this;
8935         while (parent.parent()) {
8936             parent = parent.parent();
8937             if (typeof(parent.labelAlign) !='undefined') {
8938                 return parent.labelAlign;
8939             }
8940         }
8941         return 'left';
8942         
8943     },
8944     
8945     getAutoCreate : function()
8946     {
8947         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8948         
8949         var id = Roo.id();
8950         
8951         var cfg = {};
8952         
8953         if(this.inputType != 'hidden'){
8954             cfg.cls = 'form-group' //input-group
8955         }
8956         
8957         var input =  {
8958             tag: 'input',
8959             id : id,
8960             type : this.inputType,
8961             value : this.value,
8962             cls : 'form-control',
8963             placeholder : this.placeholder || '',
8964             autocomplete : this.autocomplete || 'new-password'
8965         };
8966         
8967         if(this.capture.length){
8968             input.capture = this.capture;
8969         }
8970         
8971         if(this.accept.length){
8972             input.accept = this.accept + "/*";
8973         }
8974         
8975         if(this.align){
8976             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8977         }
8978         
8979         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8980             input.maxLength = this.maxLength;
8981         }
8982         
8983         if (this.disabled) {
8984             input.disabled=true;
8985         }
8986         
8987         if (this.readOnly) {
8988             input.readonly=true;
8989         }
8990         
8991         if (this.name) {
8992             input.name = this.name;
8993         }
8994         
8995         if (this.size) {
8996             input.cls += ' input-' + this.size;
8997         }
8998         
8999         var settings=this;
9000         ['xs','sm','md','lg'].map(function(size){
9001             if (settings[size]) {
9002                 cfg.cls += ' col-' + size + '-' + settings[size];
9003             }
9004         });
9005         
9006         var inputblock = input;
9007         
9008         var feedback = {
9009             tag: 'span',
9010             cls: 'glyphicon form-control-feedback'
9011         };
9012             
9013         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9014             
9015             inputblock = {
9016                 cls : 'has-feedback',
9017                 cn :  [
9018                     input,
9019                     feedback
9020                 ] 
9021             };  
9022         }
9023         
9024         if (this.before || this.after) {
9025             
9026             inputblock = {
9027                 cls : 'input-group',
9028                 cn :  [] 
9029             };
9030             
9031             if (this.before && typeof(this.before) == 'string') {
9032                 
9033                 inputblock.cn.push({
9034                     tag :'span',
9035                     cls : 'roo-input-before input-group-addon',
9036                     html : this.before
9037                 });
9038             }
9039             if (this.before && typeof(this.before) == 'object') {
9040                 this.before = Roo.factory(this.before);
9041                 
9042                 inputblock.cn.push({
9043                     tag :'span',
9044                     cls : 'roo-input-before input-group-' +
9045                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9046                 });
9047             }
9048             
9049             inputblock.cn.push(input);
9050             
9051             if (this.after && typeof(this.after) == 'string') {
9052                 inputblock.cn.push({
9053                     tag :'span',
9054                     cls : 'roo-input-after input-group-addon',
9055                     html : this.after
9056                 });
9057             }
9058             if (this.after && typeof(this.after) == 'object') {
9059                 this.after = Roo.factory(this.after);
9060                 
9061                 inputblock.cn.push({
9062                     tag :'span',
9063                     cls : 'roo-input-after input-group-' +
9064                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9065                 });
9066             }
9067             
9068             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9069                 inputblock.cls += ' has-feedback';
9070                 inputblock.cn.push(feedback);
9071             }
9072         };
9073         
9074         if (align ==='left' && this.fieldLabel.length) {
9075             
9076             cfg.cls += ' roo-form-group-label-left';
9077             
9078             cfg.cn = [
9079                 {
9080                     tag : 'i',
9081                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9082                     tooltip : 'This field is required'
9083                 },
9084                 {
9085                     tag: 'label',
9086                     'for' :  id,
9087                     cls : 'control-label',
9088                     html : this.fieldLabel
9089
9090                 },
9091                 {
9092                     cls : "", 
9093                     cn: [
9094                         inputblock
9095                     ]
9096                 }
9097             ];
9098             
9099             var labelCfg = cfg.cn[1];
9100             var contentCfg = cfg.cn[2];
9101             
9102             if(this.indicatorpos == 'right'){
9103                 cfg.cn = [
9104                     {
9105                         tag: 'label',
9106                         'for' :  id,
9107                         cls : 'control-label',
9108                         cn : [
9109                             {
9110                                 tag : 'span',
9111                                 html : this.fieldLabel
9112                             },
9113                             {
9114                                 tag : 'i',
9115                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9116                                 tooltip : 'This field is required'
9117                             }
9118                         ]
9119                     },
9120                     {
9121                         cls : "",
9122                         cn: [
9123                             inputblock
9124                         ]
9125                     }
9126
9127                 ];
9128                 
9129                 labelCfg = cfg.cn[0];
9130                 contentCfg = cfg.cn[1];
9131             
9132             }
9133             
9134             if(this.labelWidth > 12){
9135                 labelCfg.style = "width: " + this.labelWidth + 'px';
9136             }
9137             
9138             if(this.labelWidth < 13 && this.labelmd == 0){
9139                 this.labelmd = this.labelWidth;
9140             }
9141             
9142             if(this.labellg > 0){
9143                 labelCfg.cls += ' col-lg-' + this.labellg;
9144                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9145             }
9146             
9147             if(this.labelmd > 0){
9148                 labelCfg.cls += ' col-md-' + this.labelmd;
9149                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9150             }
9151             
9152             if(this.labelsm > 0){
9153                 labelCfg.cls += ' col-sm-' + this.labelsm;
9154                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9155             }
9156             
9157             if(this.labelxs > 0){
9158                 labelCfg.cls += ' col-xs-' + this.labelxs;
9159                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9160             }
9161             
9162             
9163         } else if ( this.fieldLabel.length) {
9164                 
9165             cfg.cn = [
9166                 {
9167                     tag : 'i',
9168                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9169                     tooltip : 'This field is required'
9170                 },
9171                 {
9172                     tag: 'label',
9173                    //cls : 'input-group-addon',
9174                     html : this.fieldLabel
9175
9176                 },
9177
9178                inputblock
9179
9180            ];
9181            
9182            if(this.indicatorpos == 'right'){
9183                 
9184                 cfg.cn = [
9185                     {
9186                         tag: 'label',
9187                        //cls : 'input-group-addon',
9188                         html : this.fieldLabel
9189
9190                     },
9191                     {
9192                         tag : 'i',
9193                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9194                         tooltip : 'This field is required'
9195                     },
9196
9197                    inputblock
9198
9199                ];
9200
9201             }
9202
9203         } else {
9204             
9205             cfg.cn = [
9206
9207                     inputblock
9208
9209             ];
9210                 
9211                 
9212         };
9213         
9214         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9215            cfg.cls += ' navbar-form';
9216         }
9217         
9218         if (this.parentType === 'NavGroup') {
9219            cfg.cls += ' navbar-form';
9220            cfg.tag = 'li';
9221         }
9222         
9223         return cfg;
9224         
9225     },
9226     /**
9227      * return the real input element.
9228      */
9229     inputEl: function ()
9230     {
9231         return this.el.select('input.form-control',true).first();
9232     },
9233     
9234     tooltipEl : function()
9235     {
9236         return this.inputEl();
9237     },
9238     
9239     indicatorEl : function()
9240     {
9241         var indicator = this.el.select('i.roo-required-indicator',true).first();
9242         
9243         if(!indicator){
9244             return false;
9245         }
9246         
9247         return indicator;
9248         
9249     },
9250     
9251     setDisabled : function(v)
9252     {
9253         var i  = this.inputEl().dom;
9254         if (!v) {
9255             i.removeAttribute('disabled');
9256             return;
9257             
9258         }
9259         i.setAttribute('disabled','true');
9260     },
9261     initEvents : function()
9262     {
9263           
9264         this.inputEl().on("keydown" , this.fireKey,  this);
9265         this.inputEl().on("focus", this.onFocus,  this);
9266         this.inputEl().on("blur", this.onBlur,  this);
9267         
9268         this.inputEl().relayEvent('keyup', this);
9269         
9270         this.indicator = this.indicatorEl();
9271         
9272         if(this.indicator){
9273             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9274         }
9275  
9276         // reference to original value for reset
9277         this.originalValue = this.getValue();
9278         //Roo.form.TextField.superclass.initEvents.call(this);
9279         if(this.validationEvent == 'keyup'){
9280             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9281             this.inputEl().on('keyup', this.filterValidation, this);
9282         }
9283         else if(this.validationEvent !== false){
9284             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9285         }
9286         
9287         if(this.selectOnFocus){
9288             this.on("focus", this.preFocus, this);
9289             
9290         }
9291         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9292             this.inputEl().on("keypress", this.filterKeys, this);
9293         } else {
9294             this.inputEl().relayEvent('keypress', this);
9295         }
9296        /* if(this.grow){
9297             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9298             this.el.on("click", this.autoSize,  this);
9299         }
9300         */
9301         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9302             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9303         }
9304         
9305         if (typeof(this.before) == 'object') {
9306             this.before.render(this.el.select('.roo-input-before',true).first());
9307         }
9308         if (typeof(this.after) == 'object') {
9309             this.after.render(this.el.select('.roo-input-after',true).first());
9310         }
9311         
9312         this.inputEl().on('change', this.onChange, this);
9313         
9314     },
9315     filterValidation : function(e){
9316         if(!e.isNavKeyPress()){
9317             this.validationTask.delay(this.validationDelay);
9318         }
9319     },
9320      /**
9321      * Validates the field value
9322      * @return {Boolean} True if the value is valid, else false
9323      */
9324     validate : function(){
9325         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9326         if(this.disabled || this.validateValue(this.getRawValue())){
9327             this.markValid();
9328             return true;
9329         }
9330         
9331         this.markInvalid();
9332         return false;
9333     },
9334     
9335     
9336     /**
9337      * Validates a value according to the field's validation rules and marks the field as invalid
9338      * if the validation fails
9339      * @param {Mixed} value The value to validate
9340      * @return {Boolean} True if the value is valid, else false
9341      */
9342     validateValue : function(value)
9343     {
9344         if(this.getVisibilityEl().hasClass('hidden')){
9345             return true;
9346         }
9347         
9348         if(value.length < 1)  { // if it's blank
9349             if(this.allowBlank){
9350                 return true;
9351             }
9352             return false;
9353         }
9354         
9355         if(value.length < this.minLength){
9356             return false;
9357         }
9358         if(value.length > this.maxLength){
9359             return false;
9360         }
9361         if(this.vtype){
9362             var vt = Roo.form.VTypes;
9363             if(!vt[this.vtype](value, this)){
9364                 return false;
9365             }
9366         }
9367         if(typeof this.validator == "function"){
9368             var msg = this.validator(value);
9369             if(msg !== true){
9370                 return false;
9371             }
9372             if (typeof(msg) == 'string') {
9373                 this.invalidText = msg;
9374             }
9375         }
9376         
9377         if(this.regex && !this.regex.test(value)){
9378             return false;
9379         }
9380         
9381         return true;
9382     },
9383     
9384      // private
9385     fireKey : function(e){
9386         //Roo.log('field ' + e.getKey());
9387         if(e.isNavKeyPress()){
9388             this.fireEvent("specialkey", this, e);
9389         }
9390     },
9391     focus : function (selectText){
9392         if(this.rendered){
9393             this.inputEl().focus();
9394             if(selectText === true){
9395                 this.inputEl().dom.select();
9396             }
9397         }
9398         return this;
9399     } ,
9400     
9401     onFocus : function(){
9402         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9403            // this.el.addClass(this.focusClass);
9404         }
9405         if(!this.hasFocus){
9406             this.hasFocus = true;
9407             this.startValue = this.getValue();
9408             this.fireEvent("focus", this);
9409         }
9410     },
9411     
9412     beforeBlur : Roo.emptyFn,
9413
9414     
9415     // private
9416     onBlur : function(){
9417         this.beforeBlur();
9418         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9419             //this.el.removeClass(this.focusClass);
9420         }
9421         this.hasFocus = false;
9422         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9423             this.validate();
9424         }
9425         var v = this.getValue();
9426         if(String(v) !== String(this.startValue)){
9427             this.fireEvent('change', this, v, this.startValue);
9428         }
9429         this.fireEvent("blur", this);
9430     },
9431     
9432     onChange : function(e)
9433     {
9434         var v = this.getValue();
9435         if(String(v) !== String(this.startValue)){
9436             this.fireEvent('change', this, v, this.startValue);
9437         }
9438         
9439     },
9440     
9441     /**
9442      * Resets the current field value to the originally loaded value and clears any validation messages
9443      */
9444     reset : function(){
9445         this.setValue(this.originalValue);
9446         this.validate();
9447     },
9448      /**
9449      * Returns the name of the field
9450      * @return {Mixed} name The name field
9451      */
9452     getName: function(){
9453         return this.name;
9454     },
9455      /**
9456      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9457      * @return {Mixed} value The field value
9458      */
9459     getValue : function(){
9460         
9461         var v = this.inputEl().getValue();
9462         
9463         return v;
9464     },
9465     /**
9466      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9467      * @return {Mixed} value The field value
9468      */
9469     getRawValue : function(){
9470         var v = this.inputEl().getValue();
9471         
9472         return v;
9473     },
9474     
9475     /**
9476      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9477      * @param {Mixed} value The value to set
9478      */
9479     setRawValue : function(v){
9480         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9481     },
9482     
9483     selectText : function(start, end){
9484         var v = this.getRawValue();
9485         if(v.length > 0){
9486             start = start === undefined ? 0 : start;
9487             end = end === undefined ? v.length : end;
9488             var d = this.inputEl().dom;
9489             if(d.setSelectionRange){
9490                 d.setSelectionRange(start, end);
9491             }else if(d.createTextRange){
9492                 var range = d.createTextRange();
9493                 range.moveStart("character", start);
9494                 range.moveEnd("character", v.length-end);
9495                 range.select();
9496             }
9497         }
9498     },
9499     
9500     /**
9501      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9502      * @param {Mixed} value The value to set
9503      */
9504     setValue : function(v){
9505         this.value = v;
9506         if(this.rendered){
9507             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9508             this.validate();
9509         }
9510     },
9511     
9512     /*
9513     processValue : function(value){
9514         if(this.stripCharsRe){
9515             var newValue = value.replace(this.stripCharsRe, '');
9516             if(newValue !== value){
9517                 this.setRawValue(newValue);
9518                 return newValue;
9519             }
9520         }
9521         return value;
9522     },
9523   */
9524     preFocus : function(){
9525         
9526         if(this.selectOnFocus){
9527             this.inputEl().dom.select();
9528         }
9529     },
9530     filterKeys : function(e){
9531         var k = e.getKey();
9532         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9533             return;
9534         }
9535         var c = e.getCharCode(), cc = String.fromCharCode(c);
9536         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9537             return;
9538         }
9539         if(!this.maskRe.test(cc)){
9540             e.stopEvent();
9541         }
9542     },
9543      /**
9544      * Clear any invalid styles/messages for this field
9545      */
9546     clearInvalid : function(){
9547         
9548         if(!this.el || this.preventMark){ // not rendered
9549             return;
9550         }
9551         
9552      
9553         this.el.removeClass(this.invalidClass);
9554         
9555         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9556             
9557             var feedback = this.el.select('.form-control-feedback', true).first();
9558             
9559             if(feedback){
9560                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9561             }
9562             
9563         }
9564         
9565         if(this.indicator){
9566             this.indicator.removeClass('visible');
9567             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9568         }
9569         
9570         this.fireEvent('valid', this);
9571     },
9572     
9573      /**
9574      * Mark this field as valid
9575      */
9576     markValid : function()
9577     {
9578         if(!this.el  || this.preventMark){ // not rendered...
9579             return;
9580         }
9581         
9582         this.el.removeClass([this.invalidClass, this.validClass]);
9583         
9584         var feedback = this.el.select('.form-control-feedback', true).first();
9585             
9586         if(feedback){
9587             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9588         }
9589         
9590         if(this.indicator){
9591             this.indicator.removeClass('visible');
9592             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9593         }
9594         
9595         if(this.disabled){
9596             return;
9597         }
9598         
9599         if(this.allowBlank && !this.getRawValue().length){
9600             return;
9601         }
9602         
9603         this.el.addClass(this.validClass);
9604         
9605         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9606             
9607             var feedback = this.el.select('.form-control-feedback', true).first();
9608             
9609             if(feedback){
9610                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9611                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9612             }
9613             
9614         }
9615         
9616         this.fireEvent('valid', this);
9617     },
9618     
9619      /**
9620      * Mark this field as invalid
9621      * @param {String} msg The validation message
9622      */
9623     markInvalid : function(msg)
9624     {
9625         if(!this.el  || this.preventMark){ // not rendered
9626             return;
9627         }
9628         
9629         this.el.removeClass([this.invalidClass, this.validClass]);
9630         
9631         var feedback = this.el.select('.form-control-feedback', true).first();
9632             
9633         if(feedback){
9634             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9635         }
9636
9637         if(this.disabled){
9638             return;
9639         }
9640         
9641         if(this.allowBlank && !this.getRawValue().length){
9642             return;
9643         }
9644         
9645         if(this.indicator){
9646             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9647             this.indicator.addClass('visible');
9648         }
9649         
9650         this.el.addClass(this.invalidClass);
9651         
9652         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9653             
9654             var feedback = this.el.select('.form-control-feedback', true).first();
9655             
9656             if(feedback){
9657                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9658                 
9659                 if(this.getValue().length || this.forceFeedback){
9660                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9661                 }
9662                 
9663             }
9664             
9665         }
9666         
9667         this.fireEvent('invalid', this, msg);
9668     },
9669     // private
9670     SafariOnKeyDown : function(event)
9671     {
9672         // this is a workaround for a password hang bug on chrome/ webkit.
9673         if (this.inputEl().dom.type != 'password') {
9674             return;
9675         }
9676         
9677         var isSelectAll = false;
9678         
9679         if(this.inputEl().dom.selectionEnd > 0){
9680             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9681         }
9682         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9683             event.preventDefault();
9684             this.setValue('');
9685             return;
9686         }
9687         
9688         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9689             
9690             event.preventDefault();
9691             // this is very hacky as keydown always get's upper case.
9692             //
9693             var cc = String.fromCharCode(event.getCharCode());
9694             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9695             
9696         }
9697     },
9698     adjustWidth : function(tag, w){
9699         tag = tag.toLowerCase();
9700         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9701             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9702                 if(tag == 'input'){
9703                     return w + 2;
9704                 }
9705                 if(tag == 'textarea'){
9706                     return w-2;
9707                 }
9708             }else if(Roo.isOpera){
9709                 if(tag == 'input'){
9710                     return w + 2;
9711                 }
9712                 if(tag == 'textarea'){
9713                     return w-2;
9714                 }
9715             }
9716         }
9717         return w;
9718     },
9719     
9720     setFieldLabel : function(v)
9721     {
9722         if(!this.rendered){
9723             return;
9724         }
9725         
9726         if(this.indicator){
9727             var ar = this.el.select('label > span',true);
9728             
9729             if (ar.elements.length) {
9730                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9731                 this.fieldLabel = v;
9732                 return;
9733             }
9734             
9735             var br = this.el.select('label',true);
9736             
9737             if(br.elements.length) {
9738                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9739                 this.fieldLabel = v;
9740                 return;
9741             }
9742             
9743             Roo.log('Cannot Found any of label > span || label in input');
9744             return;
9745         }
9746         
9747         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9748         this.fieldLabel = v;
9749         
9750         
9751     }
9752 });
9753
9754  
9755 /*
9756  * - LGPL
9757  *
9758  * Input
9759  * 
9760  */
9761
9762 /**
9763  * @class Roo.bootstrap.TextArea
9764  * @extends Roo.bootstrap.Input
9765  * Bootstrap TextArea class
9766  * @cfg {Number} cols Specifies the visible width of a text area
9767  * @cfg {Number} rows Specifies the visible number of lines in a text area
9768  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9769  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9770  * @cfg {string} html text
9771  * 
9772  * @constructor
9773  * Create a new TextArea
9774  * @param {Object} config The config object
9775  */
9776
9777 Roo.bootstrap.TextArea = function(config){
9778     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9779    
9780 };
9781
9782 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9783      
9784     cols : false,
9785     rows : 5,
9786     readOnly : false,
9787     warp : 'soft',
9788     resize : false,
9789     value: false,
9790     html: false,
9791     
9792     getAutoCreate : function(){
9793         
9794         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9795         
9796         var id = Roo.id();
9797         
9798         var cfg = {};
9799         
9800         if(this.inputType != 'hidden'){
9801             cfg.cls = 'form-group' //input-group
9802         }
9803         
9804         var input =  {
9805             tag: 'textarea',
9806             id : id,
9807             warp : this.warp,
9808             rows : this.rows,
9809             value : this.value || '',
9810             html: this.html || '',
9811             cls : 'form-control',
9812             placeholder : this.placeholder || '' 
9813             
9814         };
9815         
9816         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9817             input.maxLength = this.maxLength;
9818         }
9819         
9820         if(this.resize){
9821             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9822         }
9823         
9824         if(this.cols){
9825             input.cols = this.cols;
9826         }
9827         
9828         if (this.readOnly) {
9829             input.readonly = true;
9830         }
9831         
9832         if (this.name) {
9833             input.name = this.name;
9834         }
9835         
9836         if (this.size) {
9837             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9838         }
9839         
9840         var settings=this;
9841         ['xs','sm','md','lg'].map(function(size){
9842             if (settings[size]) {
9843                 cfg.cls += ' col-' + size + '-' + settings[size];
9844             }
9845         });
9846         
9847         var inputblock = input;
9848         
9849         if(this.hasFeedback && !this.allowBlank){
9850             
9851             var feedback = {
9852                 tag: 'span',
9853                 cls: 'glyphicon form-control-feedback'
9854             };
9855
9856             inputblock = {
9857                 cls : 'has-feedback',
9858                 cn :  [
9859                     input,
9860                     feedback
9861                 ] 
9862             };  
9863         }
9864         
9865         
9866         if (this.before || this.after) {
9867             
9868             inputblock = {
9869                 cls : 'input-group',
9870                 cn :  [] 
9871             };
9872             if (this.before) {
9873                 inputblock.cn.push({
9874                     tag :'span',
9875                     cls : 'input-group-addon',
9876                     html : this.before
9877                 });
9878             }
9879             
9880             inputblock.cn.push(input);
9881             
9882             if(this.hasFeedback && !this.allowBlank){
9883                 inputblock.cls += ' has-feedback';
9884                 inputblock.cn.push(feedback);
9885             }
9886             
9887             if (this.after) {
9888                 inputblock.cn.push({
9889                     tag :'span',
9890                     cls : 'input-group-addon',
9891                     html : this.after
9892                 });
9893             }
9894             
9895         }
9896         
9897         if (align ==='left' && this.fieldLabel.length) {
9898             cfg.cn = [
9899                 {
9900                     tag: 'label',
9901                     'for' :  id,
9902                     cls : 'control-label',
9903                     html : this.fieldLabel
9904                 },
9905                 {
9906                     cls : "",
9907                     cn: [
9908                         inputblock
9909                     ]
9910                 }
9911
9912             ];
9913             
9914             if(this.labelWidth > 12){
9915                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9916             }
9917
9918             if(this.labelWidth < 13 && this.labelmd == 0){
9919                 this.labelmd = this.labelWidth;
9920             }
9921
9922             if(this.labellg > 0){
9923                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9924                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9925             }
9926
9927             if(this.labelmd > 0){
9928                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9929                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9930             }
9931
9932             if(this.labelsm > 0){
9933                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9934                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9935             }
9936
9937             if(this.labelxs > 0){
9938                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9939                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9940             }
9941             
9942         } else if ( this.fieldLabel.length) {
9943             cfg.cn = [
9944
9945                {
9946                    tag: 'label',
9947                    //cls : 'input-group-addon',
9948                    html : this.fieldLabel
9949
9950                },
9951
9952                inputblock
9953
9954            ];
9955
9956         } else {
9957
9958             cfg.cn = [
9959
9960                 inputblock
9961
9962             ];
9963                 
9964         }
9965         
9966         if (this.disabled) {
9967             input.disabled=true;
9968         }
9969         
9970         return cfg;
9971         
9972     },
9973     /**
9974      * return the real textarea element.
9975      */
9976     inputEl: function ()
9977     {
9978         return this.el.select('textarea.form-control',true).first();
9979     },
9980     
9981     /**
9982      * Clear any invalid styles/messages for this field
9983      */
9984     clearInvalid : function()
9985     {
9986         
9987         if(!this.el || this.preventMark){ // not rendered
9988             return;
9989         }
9990         
9991         var label = this.el.select('label', true).first();
9992         var icon = this.el.select('i.fa-star', true).first();
9993         
9994         if(label && icon){
9995             icon.remove();
9996         }
9997         
9998         this.el.removeClass(this.invalidClass);
9999         
10000         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10001             
10002             var feedback = this.el.select('.form-control-feedback', true).first();
10003             
10004             if(feedback){
10005                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10006             }
10007             
10008         }
10009         
10010         this.fireEvent('valid', this);
10011     },
10012     
10013      /**
10014      * Mark this field as valid
10015      */
10016     markValid : function()
10017     {
10018         if(!this.el  || this.preventMark){ // not rendered
10019             return;
10020         }
10021         
10022         this.el.removeClass([this.invalidClass, this.validClass]);
10023         
10024         var feedback = this.el.select('.form-control-feedback', true).first();
10025             
10026         if(feedback){
10027             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10028         }
10029
10030         if(this.disabled || this.allowBlank){
10031             return;
10032         }
10033         
10034         var label = this.el.select('label', true).first();
10035         var icon = this.el.select('i.fa-star', true).first();
10036         
10037         if(label && icon){
10038             icon.remove();
10039         }
10040         
10041         this.el.addClass(this.validClass);
10042         
10043         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10044             
10045             var feedback = this.el.select('.form-control-feedback', true).first();
10046             
10047             if(feedback){
10048                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10049                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10050             }
10051             
10052         }
10053         
10054         this.fireEvent('valid', this);
10055     },
10056     
10057      /**
10058      * Mark this field as invalid
10059      * @param {String} msg The validation message
10060      */
10061     markInvalid : function(msg)
10062     {
10063         if(!this.el  || this.preventMark){ // not rendered
10064             return;
10065         }
10066         
10067         this.el.removeClass([this.invalidClass, this.validClass]);
10068         
10069         var feedback = this.el.select('.form-control-feedback', true).first();
10070             
10071         if(feedback){
10072             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10073         }
10074
10075         if(this.disabled || this.allowBlank){
10076             return;
10077         }
10078         
10079         var label = this.el.select('label', true).first();
10080         var icon = this.el.select('i.fa-star', true).first();
10081         
10082         if(!this.getValue().length && label && !icon){
10083             this.el.createChild({
10084                 tag : 'i',
10085                 cls : 'text-danger fa fa-lg fa-star',
10086                 tooltip : 'This field is required',
10087                 style : 'margin-right:5px;'
10088             }, label, true);
10089         }
10090
10091         this.el.addClass(this.invalidClass);
10092         
10093         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10094             
10095             var feedback = this.el.select('.form-control-feedback', true).first();
10096             
10097             if(feedback){
10098                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10099                 
10100                 if(this.getValue().length || this.forceFeedback){
10101                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10102                 }
10103                 
10104             }
10105             
10106         }
10107         
10108         this.fireEvent('invalid', this, msg);
10109     }
10110 });
10111
10112  
10113 /*
10114  * - LGPL
10115  *
10116  * trigger field - base class for combo..
10117  * 
10118  */
10119  
10120 /**
10121  * @class Roo.bootstrap.TriggerField
10122  * @extends Roo.bootstrap.Input
10123  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10124  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10125  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10126  * for which you can provide a custom implementation.  For example:
10127  * <pre><code>
10128 var trigger = new Roo.bootstrap.TriggerField();
10129 trigger.onTriggerClick = myTriggerFn;
10130 trigger.applyTo('my-field');
10131 </code></pre>
10132  *
10133  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10134  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10135  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10136  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10137  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10138
10139  * @constructor
10140  * Create a new TriggerField.
10141  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10142  * to the base TextField)
10143  */
10144 Roo.bootstrap.TriggerField = function(config){
10145     this.mimicing = false;
10146     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10147 };
10148
10149 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10150     /**
10151      * @cfg {String} triggerClass A CSS class to apply to the trigger
10152      */
10153      /**
10154      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10155      */
10156     hideTrigger:false,
10157
10158     /**
10159      * @cfg {Boolean} removable (true|false) special filter default false
10160      */
10161     removable : false,
10162     
10163     /** @cfg {Boolean} grow @hide */
10164     /** @cfg {Number} growMin @hide */
10165     /** @cfg {Number} growMax @hide */
10166
10167     /**
10168      * @hide 
10169      * @method
10170      */
10171     autoSize: Roo.emptyFn,
10172     // private
10173     monitorTab : true,
10174     // private
10175     deferHeight : true,
10176
10177     
10178     actionMode : 'wrap',
10179     
10180     caret : false,
10181     
10182     
10183     getAutoCreate : function(){
10184        
10185         var align = this.labelAlign || this.parentLabelAlign();
10186         
10187         var id = Roo.id();
10188         
10189         var cfg = {
10190             cls: 'form-group' //input-group
10191         };
10192         
10193         
10194         var input =  {
10195             tag: 'input',
10196             id : id,
10197             type : this.inputType,
10198             cls : 'form-control',
10199             autocomplete: 'new-password',
10200             placeholder : this.placeholder || '' 
10201             
10202         };
10203         if (this.name) {
10204             input.name = this.name;
10205         }
10206         if (this.size) {
10207             input.cls += ' input-' + this.size;
10208         }
10209         
10210         if (this.disabled) {
10211             input.disabled=true;
10212         }
10213         
10214         var inputblock = input;
10215         
10216         if(this.hasFeedback && !this.allowBlank){
10217             
10218             var feedback = {
10219                 tag: 'span',
10220                 cls: 'glyphicon form-control-feedback'
10221             };
10222             
10223             if(this.removable && !this.editable && !this.tickable){
10224                 inputblock = {
10225                     cls : 'has-feedback',
10226                     cn :  [
10227                         inputblock,
10228                         {
10229                             tag: 'button',
10230                             html : 'x',
10231                             cls : 'roo-combo-removable-btn close'
10232                         },
10233                         feedback
10234                     ] 
10235                 };
10236             } else {
10237                 inputblock = {
10238                     cls : 'has-feedback',
10239                     cn :  [
10240                         inputblock,
10241                         feedback
10242                     ] 
10243                 };
10244             }
10245
10246         } else {
10247             if(this.removable && !this.editable && !this.tickable){
10248                 inputblock = {
10249                     cls : 'roo-removable',
10250                     cn :  [
10251                         inputblock,
10252                         {
10253                             tag: 'button',
10254                             html : 'x',
10255                             cls : 'roo-combo-removable-btn close'
10256                         }
10257                     ] 
10258                 };
10259             }
10260         }
10261         
10262         if (this.before || this.after) {
10263             
10264             inputblock = {
10265                 cls : 'input-group',
10266                 cn :  [] 
10267             };
10268             if (this.before) {
10269                 inputblock.cn.push({
10270                     tag :'span',
10271                     cls : 'input-group-addon',
10272                     html : this.before
10273                 });
10274             }
10275             
10276             inputblock.cn.push(input);
10277             
10278             if(this.hasFeedback && !this.allowBlank){
10279                 inputblock.cls += ' has-feedback';
10280                 inputblock.cn.push(feedback);
10281             }
10282             
10283             if (this.after) {
10284                 inputblock.cn.push({
10285                     tag :'span',
10286                     cls : 'input-group-addon',
10287                     html : this.after
10288                 });
10289             }
10290             
10291         };
10292         
10293         var box = {
10294             tag: 'div',
10295             cn: [
10296                 {
10297                     tag: 'input',
10298                     type : 'hidden',
10299                     cls: 'form-hidden-field'
10300                 },
10301                 inputblock
10302             ]
10303             
10304         };
10305         
10306         if(this.multiple){
10307             box = {
10308                 tag: 'div',
10309                 cn: [
10310                     {
10311                         tag: 'input',
10312                         type : 'hidden',
10313                         cls: 'form-hidden-field'
10314                     },
10315                     {
10316                         tag: 'ul',
10317                         cls: 'roo-select2-choices',
10318                         cn:[
10319                             {
10320                                 tag: 'li',
10321                                 cls: 'roo-select2-search-field',
10322                                 cn: [
10323
10324                                     inputblock
10325                                 ]
10326                             }
10327                         ]
10328                     }
10329                 ]
10330             }
10331         };
10332         
10333         var combobox = {
10334             cls: 'roo-select2-container input-group',
10335             cn: [
10336                 box
10337 //                {
10338 //                    tag: 'ul',
10339 //                    cls: 'typeahead typeahead-long dropdown-menu',
10340 //                    style: 'display:none'
10341 //                }
10342             ]
10343         };
10344         
10345         if(!this.multiple && this.showToggleBtn){
10346             
10347             var caret = {
10348                         tag: 'span',
10349                         cls: 'caret'
10350              };
10351             if (this.caret != false) {
10352                 caret = {
10353                      tag: 'i',
10354                      cls: 'fa fa-' + this.caret
10355                 };
10356                 
10357             }
10358             
10359             combobox.cn.push({
10360                 tag :'span',
10361                 cls : 'input-group-addon btn dropdown-toggle',
10362                 cn : [
10363                     caret,
10364                     {
10365                         tag: 'span',
10366                         cls: 'combobox-clear',
10367                         cn  : [
10368                             {
10369                                 tag : 'i',
10370                                 cls: 'icon-remove'
10371                             }
10372                         ]
10373                     }
10374                 ]
10375
10376             })
10377         }
10378         
10379         if(this.multiple){
10380             combobox.cls += ' roo-select2-container-multi';
10381         }
10382         
10383         if (align ==='left' && this.fieldLabel.length) {
10384             
10385             cfg.cls += ' roo-form-group-label-left';
10386
10387             cfg.cn = [
10388                 {
10389                     tag : 'i',
10390                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10391                     tooltip : 'This field is required'
10392                 },
10393                 {
10394                     tag: 'label',
10395                     'for' :  id,
10396                     cls : 'control-label',
10397                     html : this.fieldLabel
10398
10399                 },
10400                 {
10401                     cls : "", 
10402                     cn: [
10403                         combobox
10404                     ]
10405                 }
10406
10407             ];
10408             
10409             var labelCfg = cfg.cn[1];
10410             var contentCfg = cfg.cn[2];
10411             
10412             if(this.indicatorpos == 'right'){
10413                 cfg.cn = [
10414                     {
10415                         tag: 'label',
10416                         'for' :  id,
10417                         cls : 'control-label',
10418                         cn : [
10419                             {
10420                                 tag : 'span',
10421                                 html : this.fieldLabel
10422                             },
10423                             {
10424                                 tag : 'i',
10425                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10426                                 tooltip : 'This field is required'
10427                             }
10428                         ]
10429                     },
10430                     {
10431                         cls : "", 
10432                         cn: [
10433                             combobox
10434                         ]
10435                     }
10436
10437                 ];
10438                 
10439                 labelCfg = cfg.cn[0];
10440                 contentCfg = cfg.cn[1];
10441             }
10442             
10443             if(this.labelWidth > 12){
10444                 labelCfg.style = "width: " + this.labelWidth + 'px';
10445             }
10446             
10447             if(this.labelWidth < 13 && this.labelmd == 0){
10448                 this.labelmd = this.labelWidth;
10449             }
10450             
10451             if(this.labellg > 0){
10452                 labelCfg.cls += ' col-lg-' + this.labellg;
10453                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10454             }
10455             
10456             if(this.labelmd > 0){
10457                 labelCfg.cls += ' col-md-' + this.labelmd;
10458                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10459             }
10460             
10461             if(this.labelsm > 0){
10462                 labelCfg.cls += ' col-sm-' + this.labelsm;
10463                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10464             }
10465             
10466             if(this.labelxs > 0){
10467                 labelCfg.cls += ' col-xs-' + this.labelxs;
10468                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10469             }
10470             
10471         } else if ( this.fieldLabel.length) {
10472 //                Roo.log(" label");
10473             cfg.cn = [
10474                 {
10475                    tag : 'i',
10476                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10477                    tooltip : 'This field is required'
10478                },
10479                {
10480                    tag: 'label',
10481                    //cls : 'input-group-addon',
10482                    html : this.fieldLabel
10483
10484                },
10485
10486                combobox
10487
10488             ];
10489             
10490             if(this.indicatorpos == 'right'){
10491                 
10492                 cfg.cn = [
10493                     {
10494                        tag: 'label',
10495                        cn : [
10496                            {
10497                                tag : 'span',
10498                                html : this.fieldLabel
10499                            },
10500                            {
10501                               tag : 'i',
10502                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10503                               tooltip : 'This field is required'
10504                            }
10505                        ]
10506
10507                     },
10508                     combobox
10509
10510                 ];
10511
10512             }
10513
10514         } else {
10515             
10516 //                Roo.log(" no label && no align");
10517                 cfg = combobox
10518                      
10519                 
10520         }
10521         
10522         var settings=this;
10523         ['xs','sm','md','lg'].map(function(size){
10524             if (settings[size]) {
10525                 cfg.cls += ' col-' + size + '-' + settings[size];
10526             }
10527         });
10528         
10529         return cfg;
10530         
10531     },
10532     
10533     
10534     
10535     // private
10536     onResize : function(w, h){
10537 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10538 //        if(typeof w == 'number'){
10539 //            var x = w - this.trigger.getWidth();
10540 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10541 //            this.trigger.setStyle('left', x+'px');
10542 //        }
10543     },
10544
10545     // private
10546     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10547
10548     // private
10549     getResizeEl : function(){
10550         return this.inputEl();
10551     },
10552
10553     // private
10554     getPositionEl : function(){
10555         return this.inputEl();
10556     },
10557
10558     // private
10559     alignErrorIcon : function(){
10560         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10561     },
10562
10563     // private
10564     initEvents : function(){
10565         
10566         this.createList();
10567         
10568         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10569         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10570         if(!this.multiple && this.showToggleBtn){
10571             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10572             if(this.hideTrigger){
10573                 this.trigger.setDisplayed(false);
10574             }
10575             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10576         }
10577         
10578         if(this.multiple){
10579             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10580         }
10581         
10582         if(this.removable && !this.editable && !this.tickable){
10583             var close = this.closeTriggerEl();
10584             
10585             if(close){
10586                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10587                 close.on('click', this.removeBtnClick, this, close);
10588             }
10589         }
10590         
10591         //this.trigger.addClassOnOver('x-form-trigger-over');
10592         //this.trigger.addClassOnClick('x-form-trigger-click');
10593         
10594         //if(!this.width){
10595         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10596         //}
10597     },
10598     
10599     closeTriggerEl : function()
10600     {
10601         var close = this.el.select('.roo-combo-removable-btn', true).first();
10602         return close ? close : false;
10603     },
10604     
10605     removeBtnClick : function(e, h, el)
10606     {
10607         e.preventDefault();
10608         
10609         if(this.fireEvent("remove", this) !== false){
10610             this.reset();
10611             this.fireEvent("afterremove", this)
10612         }
10613     },
10614     
10615     createList : function()
10616     {
10617         this.list = Roo.get(document.body).createChild({
10618             tag: 'ul',
10619             cls: 'typeahead typeahead-long dropdown-menu',
10620             style: 'display:none'
10621         });
10622         
10623         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10624         
10625     },
10626
10627     // private
10628     initTrigger : function(){
10629        
10630     },
10631
10632     // private
10633     onDestroy : function(){
10634         if(this.trigger){
10635             this.trigger.removeAllListeners();
10636           //  this.trigger.remove();
10637         }
10638         //if(this.wrap){
10639         //    this.wrap.remove();
10640         //}
10641         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10642     },
10643
10644     // private
10645     onFocus : function(){
10646         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10647         /*
10648         if(!this.mimicing){
10649             this.wrap.addClass('x-trigger-wrap-focus');
10650             this.mimicing = true;
10651             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10652             if(this.monitorTab){
10653                 this.el.on("keydown", this.checkTab, this);
10654             }
10655         }
10656         */
10657     },
10658
10659     // private
10660     checkTab : function(e){
10661         if(e.getKey() == e.TAB){
10662             this.triggerBlur();
10663         }
10664     },
10665
10666     // private
10667     onBlur : function(){
10668         // do nothing
10669     },
10670
10671     // private
10672     mimicBlur : function(e, t){
10673         /*
10674         if(!this.wrap.contains(t) && this.validateBlur()){
10675             this.triggerBlur();
10676         }
10677         */
10678     },
10679
10680     // private
10681     triggerBlur : function(){
10682         this.mimicing = false;
10683         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10684         if(this.monitorTab){
10685             this.el.un("keydown", this.checkTab, this);
10686         }
10687         //this.wrap.removeClass('x-trigger-wrap-focus');
10688         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10689     },
10690
10691     // private
10692     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10693     validateBlur : function(e, t){
10694         return true;
10695     },
10696
10697     // private
10698     onDisable : function(){
10699         this.inputEl().dom.disabled = true;
10700         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10701         //if(this.wrap){
10702         //    this.wrap.addClass('x-item-disabled');
10703         //}
10704     },
10705
10706     // private
10707     onEnable : function(){
10708         this.inputEl().dom.disabled = false;
10709         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10710         //if(this.wrap){
10711         //    this.el.removeClass('x-item-disabled');
10712         //}
10713     },
10714
10715     // private
10716     onShow : function(){
10717         var ae = this.getActionEl();
10718         
10719         if(ae){
10720             ae.dom.style.display = '';
10721             ae.dom.style.visibility = 'visible';
10722         }
10723     },
10724
10725     // private
10726     
10727     onHide : function(){
10728         var ae = this.getActionEl();
10729         ae.dom.style.display = 'none';
10730     },
10731
10732     /**
10733      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10734      * by an implementing function.
10735      * @method
10736      * @param {EventObject} e
10737      */
10738     onTriggerClick : Roo.emptyFn
10739 });
10740  /*
10741  * Based on:
10742  * Ext JS Library 1.1.1
10743  * Copyright(c) 2006-2007, Ext JS, LLC.
10744  *
10745  * Originally Released Under LGPL - original licence link has changed is not relivant.
10746  *
10747  * Fork - LGPL
10748  * <script type="text/javascript">
10749  */
10750
10751
10752 /**
10753  * @class Roo.data.SortTypes
10754  * @singleton
10755  * Defines the default sorting (casting?) comparison functions used when sorting data.
10756  */
10757 Roo.data.SortTypes = {
10758     /**
10759      * Default sort that does nothing
10760      * @param {Mixed} s The value being converted
10761      * @return {Mixed} The comparison value
10762      */
10763     none : function(s){
10764         return s;
10765     },
10766     
10767     /**
10768      * The regular expression used to strip tags
10769      * @type {RegExp}
10770      * @property
10771      */
10772     stripTagsRE : /<\/?[^>]+>/gi,
10773     
10774     /**
10775      * Strips all HTML tags to sort on text only
10776      * @param {Mixed} s The value being converted
10777      * @return {String} The comparison value
10778      */
10779     asText : function(s){
10780         return String(s).replace(this.stripTagsRE, "");
10781     },
10782     
10783     /**
10784      * Strips all HTML tags to sort on text only - Case insensitive
10785      * @param {Mixed} s The value being converted
10786      * @return {String} The comparison value
10787      */
10788     asUCText : function(s){
10789         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10790     },
10791     
10792     /**
10793      * Case insensitive string
10794      * @param {Mixed} s The value being converted
10795      * @return {String} The comparison value
10796      */
10797     asUCString : function(s) {
10798         return String(s).toUpperCase();
10799     },
10800     
10801     /**
10802      * Date sorting
10803      * @param {Mixed} s The value being converted
10804      * @return {Number} The comparison value
10805      */
10806     asDate : function(s) {
10807         if(!s){
10808             return 0;
10809         }
10810         if(s instanceof Date){
10811             return s.getTime();
10812         }
10813         return Date.parse(String(s));
10814     },
10815     
10816     /**
10817      * Float sorting
10818      * @param {Mixed} s The value being converted
10819      * @return {Float} The comparison value
10820      */
10821     asFloat : function(s) {
10822         var val = parseFloat(String(s).replace(/,/g, ""));
10823         if(isNaN(val)) {
10824             val = 0;
10825         }
10826         return val;
10827     },
10828     
10829     /**
10830      * Integer sorting
10831      * @param {Mixed} s The value being converted
10832      * @return {Number} The comparison value
10833      */
10834     asInt : function(s) {
10835         var val = parseInt(String(s).replace(/,/g, ""));
10836         if(isNaN(val)) {
10837             val = 0;
10838         }
10839         return val;
10840     }
10841 };/*
10842  * Based on:
10843  * Ext JS Library 1.1.1
10844  * Copyright(c) 2006-2007, Ext JS, LLC.
10845  *
10846  * Originally Released Under LGPL - original licence link has changed is not relivant.
10847  *
10848  * Fork - LGPL
10849  * <script type="text/javascript">
10850  */
10851
10852 /**
10853 * @class Roo.data.Record
10854  * Instances of this class encapsulate both record <em>definition</em> information, and record
10855  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10856  * to access Records cached in an {@link Roo.data.Store} object.<br>
10857  * <p>
10858  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10859  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10860  * objects.<br>
10861  * <p>
10862  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10863  * @constructor
10864  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10865  * {@link #create}. The parameters are the same.
10866  * @param {Array} data An associative Array of data values keyed by the field name.
10867  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10868  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10869  * not specified an integer id is generated.
10870  */
10871 Roo.data.Record = function(data, id){
10872     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10873     this.data = data;
10874 };
10875
10876 /**
10877  * Generate a constructor for a specific record layout.
10878  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10879  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10880  * Each field definition object may contain the following properties: <ul>
10881  * <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,
10882  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10883  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10884  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10885  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10886  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10887  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10888  * this may be omitted.</p></li>
10889  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10890  * <ul><li>auto (Default, implies no conversion)</li>
10891  * <li>string</li>
10892  * <li>int</li>
10893  * <li>float</li>
10894  * <li>boolean</li>
10895  * <li>date</li></ul></p></li>
10896  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10897  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10898  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10899  * by the Reader into an object that will be stored in the Record. It is passed the
10900  * following parameters:<ul>
10901  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10902  * </ul></p></li>
10903  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10904  * </ul>
10905  * <br>usage:<br><pre><code>
10906 var TopicRecord = Roo.data.Record.create(
10907     {name: 'title', mapping: 'topic_title'},
10908     {name: 'author', mapping: 'username'},
10909     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10910     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10911     {name: 'lastPoster', mapping: 'user2'},
10912     {name: 'excerpt', mapping: 'post_text'}
10913 );
10914
10915 var myNewRecord = new TopicRecord({
10916     title: 'Do my job please',
10917     author: 'noobie',
10918     totalPosts: 1,
10919     lastPost: new Date(),
10920     lastPoster: 'Animal',
10921     excerpt: 'No way dude!'
10922 });
10923 myStore.add(myNewRecord);
10924 </code></pre>
10925  * @method create
10926  * @static
10927  */
10928 Roo.data.Record.create = function(o){
10929     var f = function(){
10930         f.superclass.constructor.apply(this, arguments);
10931     };
10932     Roo.extend(f, Roo.data.Record);
10933     var p = f.prototype;
10934     p.fields = new Roo.util.MixedCollection(false, function(field){
10935         return field.name;
10936     });
10937     for(var i = 0, len = o.length; i < len; i++){
10938         p.fields.add(new Roo.data.Field(o[i]));
10939     }
10940     f.getField = function(name){
10941         return p.fields.get(name);  
10942     };
10943     return f;
10944 };
10945
10946 Roo.data.Record.AUTO_ID = 1000;
10947 Roo.data.Record.EDIT = 'edit';
10948 Roo.data.Record.REJECT = 'reject';
10949 Roo.data.Record.COMMIT = 'commit';
10950
10951 Roo.data.Record.prototype = {
10952     /**
10953      * Readonly flag - true if this record has been modified.
10954      * @type Boolean
10955      */
10956     dirty : false,
10957     editing : false,
10958     error: null,
10959     modified: null,
10960
10961     // private
10962     join : function(store){
10963         this.store = store;
10964     },
10965
10966     /**
10967      * Set the named field to the specified value.
10968      * @param {String} name The name of the field to set.
10969      * @param {Object} value The value to set the field to.
10970      */
10971     set : function(name, value){
10972         if(this.data[name] == value){
10973             return;
10974         }
10975         this.dirty = true;
10976         if(!this.modified){
10977             this.modified = {};
10978         }
10979         if(typeof this.modified[name] == 'undefined'){
10980             this.modified[name] = this.data[name];
10981         }
10982         this.data[name] = value;
10983         if(!this.editing && this.store){
10984             this.store.afterEdit(this);
10985         }       
10986     },
10987
10988     /**
10989      * Get the value of the named field.
10990      * @param {String} name The name of the field to get the value of.
10991      * @return {Object} The value of the field.
10992      */
10993     get : function(name){
10994         return this.data[name]; 
10995     },
10996
10997     // private
10998     beginEdit : function(){
10999         this.editing = true;
11000         this.modified = {}; 
11001     },
11002
11003     // private
11004     cancelEdit : function(){
11005         this.editing = false;
11006         delete this.modified;
11007     },
11008
11009     // private
11010     endEdit : function(){
11011         this.editing = false;
11012         if(this.dirty && this.store){
11013             this.store.afterEdit(this);
11014         }
11015     },
11016
11017     /**
11018      * Usually called by the {@link Roo.data.Store} which owns the Record.
11019      * Rejects all changes made to the Record since either creation, or the last commit operation.
11020      * Modified fields are reverted to their original values.
11021      * <p>
11022      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11023      * of reject operations.
11024      */
11025     reject : function(){
11026         var m = this.modified;
11027         for(var n in m){
11028             if(typeof m[n] != "function"){
11029                 this.data[n] = m[n];
11030             }
11031         }
11032         this.dirty = false;
11033         delete this.modified;
11034         this.editing = false;
11035         if(this.store){
11036             this.store.afterReject(this);
11037         }
11038     },
11039
11040     /**
11041      * Usually called by the {@link Roo.data.Store} which owns the Record.
11042      * Commits all changes made to the Record since either creation, or the last commit operation.
11043      * <p>
11044      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11045      * of commit operations.
11046      */
11047     commit : function(){
11048         this.dirty = false;
11049         delete this.modified;
11050         this.editing = false;
11051         if(this.store){
11052             this.store.afterCommit(this);
11053         }
11054     },
11055
11056     // private
11057     hasError : function(){
11058         return this.error != null;
11059     },
11060
11061     // private
11062     clearError : function(){
11063         this.error = null;
11064     },
11065
11066     /**
11067      * Creates a copy of this record.
11068      * @param {String} id (optional) A new record id if you don't want to use this record's id
11069      * @return {Record}
11070      */
11071     copy : function(newId) {
11072         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11073     }
11074 };/*
11075  * Based on:
11076  * Ext JS Library 1.1.1
11077  * Copyright(c) 2006-2007, Ext JS, LLC.
11078  *
11079  * Originally Released Under LGPL - original licence link has changed is not relivant.
11080  *
11081  * Fork - LGPL
11082  * <script type="text/javascript">
11083  */
11084
11085
11086
11087 /**
11088  * @class Roo.data.Store
11089  * @extends Roo.util.Observable
11090  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11091  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11092  * <p>
11093  * 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
11094  * has no knowledge of the format of the data returned by the Proxy.<br>
11095  * <p>
11096  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11097  * instances from the data object. These records are cached and made available through accessor functions.
11098  * @constructor
11099  * Creates a new Store.
11100  * @param {Object} config A config object containing the objects needed for the Store to access data,
11101  * and read the data into Records.
11102  */
11103 Roo.data.Store = function(config){
11104     this.data = new Roo.util.MixedCollection(false);
11105     this.data.getKey = function(o){
11106         return o.id;
11107     };
11108     this.baseParams = {};
11109     // private
11110     this.paramNames = {
11111         "start" : "start",
11112         "limit" : "limit",
11113         "sort" : "sort",
11114         "dir" : "dir",
11115         "multisort" : "_multisort"
11116     };
11117
11118     if(config && config.data){
11119         this.inlineData = config.data;
11120         delete config.data;
11121     }
11122
11123     Roo.apply(this, config);
11124     
11125     if(this.reader){ // reader passed
11126         this.reader = Roo.factory(this.reader, Roo.data);
11127         this.reader.xmodule = this.xmodule || false;
11128         if(!this.recordType){
11129             this.recordType = this.reader.recordType;
11130         }
11131         if(this.reader.onMetaChange){
11132             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11133         }
11134     }
11135
11136     if(this.recordType){
11137         this.fields = this.recordType.prototype.fields;
11138     }
11139     this.modified = [];
11140
11141     this.addEvents({
11142         /**
11143          * @event datachanged
11144          * Fires when the data cache has changed, and a widget which is using this Store
11145          * as a Record cache should refresh its view.
11146          * @param {Store} this
11147          */
11148         datachanged : true,
11149         /**
11150          * @event metachange
11151          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11152          * @param {Store} this
11153          * @param {Object} meta The JSON metadata
11154          */
11155         metachange : true,
11156         /**
11157          * @event add
11158          * Fires when Records have been added to the Store
11159          * @param {Store} this
11160          * @param {Roo.data.Record[]} records The array of Records added
11161          * @param {Number} index The index at which the record(s) were added
11162          */
11163         add : true,
11164         /**
11165          * @event remove
11166          * Fires when a Record has been removed from the Store
11167          * @param {Store} this
11168          * @param {Roo.data.Record} record The Record that was removed
11169          * @param {Number} index The index at which the record was removed
11170          */
11171         remove : true,
11172         /**
11173          * @event update
11174          * Fires when a Record has been updated
11175          * @param {Store} this
11176          * @param {Roo.data.Record} record The Record that was updated
11177          * @param {String} operation The update operation being performed.  Value may be one of:
11178          * <pre><code>
11179  Roo.data.Record.EDIT
11180  Roo.data.Record.REJECT
11181  Roo.data.Record.COMMIT
11182          * </code></pre>
11183          */
11184         update : true,
11185         /**
11186          * @event clear
11187          * Fires when the data cache has been cleared.
11188          * @param {Store} this
11189          */
11190         clear : true,
11191         /**
11192          * @event beforeload
11193          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11194          * the load action will be canceled.
11195          * @param {Store} this
11196          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11197          */
11198         beforeload : true,
11199         /**
11200          * @event beforeloadadd
11201          * Fires after a new set of Records has been loaded.
11202          * @param {Store} this
11203          * @param {Roo.data.Record[]} records The Records that were loaded
11204          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11205          */
11206         beforeloadadd : true,
11207         /**
11208          * @event load
11209          * Fires after a new set of Records has been loaded, before they are added to the store.
11210          * @param {Store} this
11211          * @param {Roo.data.Record[]} records The Records that were loaded
11212          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11213          * @params {Object} return from reader
11214          */
11215         load : true,
11216         /**
11217          * @event loadexception
11218          * Fires if an exception occurs in the Proxy during loading.
11219          * Called with the signature of the Proxy's "loadexception" event.
11220          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11221          * 
11222          * @param {Proxy} 
11223          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11224          * @param {Object} load options 
11225          * @param {Object} jsonData from your request (normally this contains the Exception)
11226          */
11227         loadexception : true
11228     });
11229     
11230     if(this.proxy){
11231         this.proxy = Roo.factory(this.proxy, Roo.data);
11232         this.proxy.xmodule = this.xmodule || false;
11233         this.relayEvents(this.proxy,  ["loadexception"]);
11234     }
11235     this.sortToggle = {};
11236     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11237
11238     Roo.data.Store.superclass.constructor.call(this);
11239
11240     if(this.inlineData){
11241         this.loadData(this.inlineData);
11242         delete this.inlineData;
11243     }
11244 };
11245
11246 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11247      /**
11248     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11249     * without a remote query - used by combo/forms at present.
11250     */
11251     
11252     /**
11253     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11254     */
11255     /**
11256     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11257     */
11258     /**
11259     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11260     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11261     */
11262     /**
11263     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11264     * on any HTTP request
11265     */
11266     /**
11267     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11268     */
11269     /**
11270     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11271     */
11272     multiSort: false,
11273     /**
11274     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11275     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11276     */
11277     remoteSort : false,
11278
11279     /**
11280     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11281      * loaded or when a record is removed. (defaults to false).
11282     */
11283     pruneModifiedRecords : false,
11284
11285     // private
11286     lastOptions : null,
11287
11288     /**
11289      * Add Records to the Store and fires the add event.
11290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11291      */
11292     add : function(records){
11293         records = [].concat(records);
11294         for(var i = 0, len = records.length; i < len; i++){
11295             records[i].join(this);
11296         }
11297         var index = this.data.length;
11298         this.data.addAll(records);
11299         this.fireEvent("add", this, records, index);
11300     },
11301
11302     /**
11303      * Remove a Record from the Store and fires the remove event.
11304      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11305      */
11306     remove : function(record){
11307         var index = this.data.indexOf(record);
11308         this.data.removeAt(index);
11309  
11310         if(this.pruneModifiedRecords){
11311             this.modified.remove(record);
11312         }
11313         this.fireEvent("remove", this, record, index);
11314     },
11315
11316     /**
11317      * Remove all Records from the Store and fires the clear event.
11318      */
11319     removeAll : function(){
11320         this.data.clear();
11321         if(this.pruneModifiedRecords){
11322             this.modified = [];
11323         }
11324         this.fireEvent("clear", this);
11325     },
11326
11327     /**
11328      * Inserts Records to the Store at the given index and fires the add event.
11329      * @param {Number} index The start index at which to insert the passed Records.
11330      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11331      */
11332     insert : function(index, records){
11333         records = [].concat(records);
11334         for(var i = 0, len = records.length; i < len; i++){
11335             this.data.insert(index, records[i]);
11336             records[i].join(this);
11337         }
11338         this.fireEvent("add", this, records, index);
11339     },
11340
11341     /**
11342      * Get the index within the cache of the passed Record.
11343      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11344      * @return {Number} The index of the passed Record. Returns -1 if not found.
11345      */
11346     indexOf : function(record){
11347         return this.data.indexOf(record);
11348     },
11349
11350     /**
11351      * Get the index within the cache of the Record with the passed id.
11352      * @param {String} id The id of the Record to find.
11353      * @return {Number} The index of the Record. Returns -1 if not found.
11354      */
11355     indexOfId : function(id){
11356         return this.data.indexOfKey(id);
11357     },
11358
11359     /**
11360      * Get the Record with the specified id.
11361      * @param {String} id The id of the Record to find.
11362      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11363      */
11364     getById : function(id){
11365         return this.data.key(id);
11366     },
11367
11368     /**
11369      * Get the Record at the specified index.
11370      * @param {Number} index The index of the Record to find.
11371      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11372      */
11373     getAt : function(index){
11374         return this.data.itemAt(index);
11375     },
11376
11377     /**
11378      * Returns a range of Records between specified indices.
11379      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11380      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11381      * @return {Roo.data.Record[]} An array of Records
11382      */
11383     getRange : function(start, end){
11384         return this.data.getRange(start, end);
11385     },
11386
11387     // private
11388     storeOptions : function(o){
11389         o = Roo.apply({}, o);
11390         delete o.callback;
11391         delete o.scope;
11392         this.lastOptions = o;
11393     },
11394
11395     /**
11396      * Loads the Record cache from the configured Proxy using the configured Reader.
11397      * <p>
11398      * If using remote paging, then the first load call must specify the <em>start</em>
11399      * and <em>limit</em> properties in the options.params property to establish the initial
11400      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11401      * <p>
11402      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11403      * and this call will return before the new data has been loaded. Perform any post-processing
11404      * in a callback function, or in a "load" event handler.</strong>
11405      * <p>
11406      * @param {Object} options An object containing properties which control loading options:<ul>
11407      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11408      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11409      * passed the following arguments:<ul>
11410      * <li>r : Roo.data.Record[]</li>
11411      * <li>options: Options object from the load call</li>
11412      * <li>success: Boolean success indicator</li></ul></li>
11413      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11414      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11415      * </ul>
11416      */
11417     load : function(options){
11418         options = options || {};
11419         if(this.fireEvent("beforeload", this, options) !== false){
11420             this.storeOptions(options);
11421             var p = Roo.apply(options.params || {}, this.baseParams);
11422             // if meta was not loaded from remote source.. try requesting it.
11423             if (!this.reader.metaFromRemote) {
11424                 p._requestMeta = 1;
11425             }
11426             if(this.sortInfo && this.remoteSort){
11427                 var pn = this.paramNames;
11428                 p[pn["sort"]] = this.sortInfo.field;
11429                 p[pn["dir"]] = this.sortInfo.direction;
11430             }
11431             if (this.multiSort) {
11432                 var pn = this.paramNames;
11433                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11434             }
11435             
11436             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11437         }
11438     },
11439
11440     /**
11441      * Reloads the Record cache from the configured Proxy using the configured Reader and
11442      * the options from the last load operation performed.
11443      * @param {Object} options (optional) An object containing properties which may override the options
11444      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11445      * the most recently used options are reused).
11446      */
11447     reload : function(options){
11448         this.load(Roo.applyIf(options||{}, this.lastOptions));
11449     },
11450
11451     // private
11452     // Called as a callback by the Reader during a load operation.
11453     loadRecords : function(o, options, success){
11454         if(!o || success === false){
11455             if(success !== false){
11456                 this.fireEvent("load", this, [], options, o);
11457             }
11458             if(options.callback){
11459                 options.callback.call(options.scope || this, [], options, false);
11460             }
11461             return;
11462         }
11463         // if data returned failure - throw an exception.
11464         if (o.success === false) {
11465             // show a message if no listener is registered.
11466             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11467                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11468             }
11469             // loadmask wil be hooked into this..
11470             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11471             return;
11472         }
11473         var r = o.records, t = o.totalRecords || r.length;
11474         
11475         this.fireEvent("beforeloadadd", this, r, options, o);
11476         
11477         if(!options || options.add !== true){
11478             if(this.pruneModifiedRecords){
11479                 this.modified = [];
11480             }
11481             for(var i = 0, len = r.length; i < len; i++){
11482                 r[i].join(this);
11483             }
11484             if(this.snapshot){
11485                 this.data = this.snapshot;
11486                 delete this.snapshot;
11487             }
11488             this.data.clear();
11489             this.data.addAll(r);
11490             this.totalLength = t;
11491             this.applySort();
11492             this.fireEvent("datachanged", this);
11493         }else{
11494             this.totalLength = Math.max(t, this.data.length+r.length);
11495             this.add(r);
11496         }
11497         
11498         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11499                 
11500             var e = new Roo.data.Record({});
11501
11502             e.set(this.parent.displayField, this.parent.emptyTitle);
11503             e.set(this.parent.valueField, '');
11504
11505             this.insert(0, e);
11506         }
11507             
11508         this.fireEvent("load", this, r, options, o);
11509         if(options.callback){
11510             options.callback.call(options.scope || this, r, options, true);
11511         }
11512     },
11513
11514
11515     /**
11516      * Loads data from a passed data block. A Reader which understands the format of the data
11517      * must have been configured in the constructor.
11518      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11519      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11520      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11521      */
11522     loadData : function(o, append){
11523         var r = this.reader.readRecords(o);
11524         this.loadRecords(r, {add: append}, true);
11525     },
11526
11527     /**
11528      * Gets the number of cached records.
11529      * <p>
11530      * <em>If using paging, this may not be the total size of the dataset. If the data object
11531      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11532      * the data set size</em>
11533      */
11534     getCount : function(){
11535         return this.data.length || 0;
11536     },
11537
11538     /**
11539      * Gets the total number of records in the dataset as returned by the server.
11540      * <p>
11541      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11542      * the dataset size</em>
11543      */
11544     getTotalCount : function(){
11545         return this.totalLength || 0;
11546     },
11547
11548     /**
11549      * Returns the sort state of the Store as an object with two properties:
11550      * <pre><code>
11551  field {String} The name of the field by which the Records are sorted
11552  direction {String} The sort order, "ASC" or "DESC"
11553      * </code></pre>
11554      */
11555     getSortState : function(){
11556         return this.sortInfo;
11557     },
11558
11559     // private
11560     applySort : function(){
11561         if(this.sortInfo && !this.remoteSort){
11562             var s = this.sortInfo, f = s.field;
11563             var st = this.fields.get(f).sortType;
11564             var fn = function(r1, r2){
11565                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11566                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11567             };
11568             this.data.sort(s.direction, fn);
11569             if(this.snapshot && this.snapshot != this.data){
11570                 this.snapshot.sort(s.direction, fn);
11571             }
11572         }
11573     },
11574
11575     /**
11576      * Sets the default sort column and order to be used by the next load operation.
11577      * @param {String} fieldName The name of the field to sort by.
11578      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11579      */
11580     setDefaultSort : function(field, dir){
11581         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11582     },
11583
11584     /**
11585      * Sort the Records.
11586      * If remote sorting is used, the sort is performed on the server, and the cache is
11587      * reloaded. If local sorting is used, the cache is sorted internally.
11588      * @param {String} fieldName The name of the field to sort by.
11589      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11590      */
11591     sort : function(fieldName, dir){
11592         var f = this.fields.get(fieldName);
11593         if(!dir){
11594             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11595             
11596             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11597                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11598             }else{
11599                 dir = f.sortDir;
11600             }
11601         }
11602         this.sortToggle[f.name] = dir;
11603         this.sortInfo = {field: f.name, direction: dir};
11604         if(!this.remoteSort){
11605             this.applySort();
11606             this.fireEvent("datachanged", this);
11607         }else{
11608             this.load(this.lastOptions);
11609         }
11610     },
11611
11612     /**
11613      * Calls the specified function for each of the Records in the cache.
11614      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11615      * Returning <em>false</em> aborts and exits the iteration.
11616      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11617      */
11618     each : function(fn, scope){
11619         this.data.each(fn, scope);
11620     },
11621
11622     /**
11623      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11624      * (e.g., during paging).
11625      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11626      */
11627     getModifiedRecords : function(){
11628         return this.modified;
11629     },
11630
11631     // private
11632     createFilterFn : function(property, value, anyMatch){
11633         if(!value.exec){ // not a regex
11634             value = String(value);
11635             if(value.length == 0){
11636                 return false;
11637             }
11638             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11639         }
11640         return function(r){
11641             return value.test(r.data[property]);
11642         };
11643     },
11644
11645     /**
11646      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11647      * @param {String} property A field on your records
11648      * @param {Number} start The record index to start at (defaults to 0)
11649      * @param {Number} end The last record index to include (defaults to length - 1)
11650      * @return {Number} The sum
11651      */
11652     sum : function(property, start, end){
11653         var rs = this.data.items, v = 0;
11654         start = start || 0;
11655         end = (end || end === 0) ? end : rs.length-1;
11656
11657         for(var i = start; i <= end; i++){
11658             v += (rs[i].data[property] || 0);
11659         }
11660         return v;
11661     },
11662
11663     /**
11664      * Filter the records by a specified property.
11665      * @param {String} field A field on your records
11666      * @param {String/RegExp} value Either a string that the field
11667      * should start with or a RegExp to test against the field
11668      * @param {Boolean} anyMatch True to match any part not just the beginning
11669      */
11670     filter : function(property, value, anyMatch){
11671         var fn = this.createFilterFn(property, value, anyMatch);
11672         return fn ? this.filterBy(fn) : this.clearFilter();
11673     },
11674
11675     /**
11676      * Filter by a function. The specified function will be called with each
11677      * record in this data source. If the function returns true the record is included,
11678      * otherwise it is filtered.
11679      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11680      * @param {Object} scope (optional) The scope of the function (defaults to this)
11681      */
11682     filterBy : function(fn, scope){
11683         this.snapshot = this.snapshot || this.data;
11684         this.data = this.queryBy(fn, scope||this);
11685         this.fireEvent("datachanged", this);
11686     },
11687
11688     /**
11689      * Query the records by a specified property.
11690      * @param {String} field A field on your records
11691      * @param {String/RegExp} value Either a string that the field
11692      * should start with or a RegExp to test against the field
11693      * @param {Boolean} anyMatch True to match any part not just the beginning
11694      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11695      */
11696     query : function(property, value, anyMatch){
11697         var fn = this.createFilterFn(property, value, anyMatch);
11698         return fn ? this.queryBy(fn) : this.data.clone();
11699     },
11700
11701     /**
11702      * Query by a function. The specified function will be called with each
11703      * record in this data source. If the function returns true the record is included
11704      * in the results.
11705      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11706      * @param {Object} scope (optional) The scope of the function (defaults to this)
11707       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11708      **/
11709     queryBy : function(fn, scope){
11710         var data = this.snapshot || this.data;
11711         return data.filterBy(fn, scope||this);
11712     },
11713
11714     /**
11715      * Collects unique values for a particular dataIndex from this store.
11716      * @param {String} dataIndex The property to collect
11717      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11718      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11719      * @return {Array} An array of the unique values
11720      **/
11721     collect : function(dataIndex, allowNull, bypassFilter){
11722         var d = (bypassFilter === true && this.snapshot) ?
11723                 this.snapshot.items : this.data.items;
11724         var v, sv, r = [], l = {};
11725         for(var i = 0, len = d.length; i < len; i++){
11726             v = d[i].data[dataIndex];
11727             sv = String(v);
11728             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11729                 l[sv] = true;
11730                 r[r.length] = v;
11731             }
11732         }
11733         return r;
11734     },
11735
11736     /**
11737      * Revert to a view of the Record cache with no filtering applied.
11738      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11739      */
11740     clearFilter : function(suppressEvent){
11741         if(this.snapshot && this.snapshot != this.data){
11742             this.data = this.snapshot;
11743             delete this.snapshot;
11744             if(suppressEvent !== true){
11745                 this.fireEvent("datachanged", this);
11746             }
11747         }
11748     },
11749
11750     // private
11751     afterEdit : function(record){
11752         if(this.modified.indexOf(record) == -1){
11753             this.modified.push(record);
11754         }
11755         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11756     },
11757     
11758     // private
11759     afterReject : function(record){
11760         this.modified.remove(record);
11761         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11762     },
11763
11764     // private
11765     afterCommit : function(record){
11766         this.modified.remove(record);
11767         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11768     },
11769
11770     /**
11771      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11772      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11773      */
11774     commitChanges : function(){
11775         var m = this.modified.slice(0);
11776         this.modified = [];
11777         for(var i = 0, len = m.length; i < len; i++){
11778             m[i].commit();
11779         }
11780     },
11781
11782     /**
11783      * Cancel outstanding changes on all changed records.
11784      */
11785     rejectChanges : function(){
11786         var m = this.modified.slice(0);
11787         this.modified = [];
11788         for(var i = 0, len = m.length; i < len; i++){
11789             m[i].reject();
11790         }
11791     },
11792
11793     onMetaChange : function(meta, rtype, o){
11794         this.recordType = rtype;
11795         this.fields = rtype.prototype.fields;
11796         delete this.snapshot;
11797         this.sortInfo = meta.sortInfo || this.sortInfo;
11798         this.modified = [];
11799         this.fireEvent('metachange', this, this.reader.meta);
11800     },
11801     
11802     moveIndex : function(data, type)
11803     {
11804         var index = this.indexOf(data);
11805         
11806         var newIndex = index + type;
11807         
11808         this.remove(data);
11809         
11810         this.insert(newIndex, data);
11811         
11812     }
11813 });/*
11814  * Based on:
11815  * Ext JS Library 1.1.1
11816  * Copyright(c) 2006-2007, Ext JS, LLC.
11817  *
11818  * Originally Released Under LGPL - original licence link has changed is not relivant.
11819  *
11820  * Fork - LGPL
11821  * <script type="text/javascript">
11822  */
11823
11824 /**
11825  * @class Roo.data.SimpleStore
11826  * @extends Roo.data.Store
11827  * Small helper class to make creating Stores from Array data easier.
11828  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11829  * @cfg {Array} fields An array of field definition objects, or field name strings.
11830  * @cfg {Array} data The multi-dimensional array of data
11831  * @constructor
11832  * @param {Object} config
11833  */
11834 Roo.data.SimpleStore = function(config){
11835     Roo.data.SimpleStore.superclass.constructor.call(this, {
11836         isLocal : true,
11837         reader: new Roo.data.ArrayReader({
11838                 id: config.id
11839             },
11840             Roo.data.Record.create(config.fields)
11841         ),
11842         proxy : new Roo.data.MemoryProxy(config.data)
11843     });
11844     this.load();
11845 };
11846 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11847  * Based on:
11848  * Ext JS Library 1.1.1
11849  * Copyright(c) 2006-2007, Ext JS, LLC.
11850  *
11851  * Originally Released Under LGPL - original licence link has changed is not relivant.
11852  *
11853  * Fork - LGPL
11854  * <script type="text/javascript">
11855  */
11856
11857 /**
11858 /**
11859  * @extends Roo.data.Store
11860  * @class Roo.data.JsonStore
11861  * Small helper class to make creating Stores for JSON data easier. <br/>
11862 <pre><code>
11863 var store = new Roo.data.JsonStore({
11864     url: 'get-images.php',
11865     root: 'images',
11866     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11867 });
11868 </code></pre>
11869  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11870  * JsonReader and HttpProxy (unless inline data is provided).</b>
11871  * @cfg {Array} fields An array of field definition objects, or field name strings.
11872  * @constructor
11873  * @param {Object} config
11874  */
11875 Roo.data.JsonStore = function(c){
11876     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11877         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11878         reader: new Roo.data.JsonReader(c, c.fields)
11879     }));
11880 };
11881 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11882  * Based on:
11883  * Ext JS Library 1.1.1
11884  * Copyright(c) 2006-2007, Ext JS, LLC.
11885  *
11886  * Originally Released Under LGPL - original licence link has changed is not relivant.
11887  *
11888  * Fork - LGPL
11889  * <script type="text/javascript">
11890  */
11891
11892  
11893 Roo.data.Field = function(config){
11894     if(typeof config == "string"){
11895         config = {name: config};
11896     }
11897     Roo.apply(this, config);
11898     
11899     if(!this.type){
11900         this.type = "auto";
11901     }
11902     
11903     var st = Roo.data.SortTypes;
11904     // named sortTypes are supported, here we look them up
11905     if(typeof this.sortType == "string"){
11906         this.sortType = st[this.sortType];
11907     }
11908     
11909     // set default sortType for strings and dates
11910     if(!this.sortType){
11911         switch(this.type){
11912             case "string":
11913                 this.sortType = st.asUCString;
11914                 break;
11915             case "date":
11916                 this.sortType = st.asDate;
11917                 break;
11918             default:
11919                 this.sortType = st.none;
11920         }
11921     }
11922
11923     // define once
11924     var stripRe = /[\$,%]/g;
11925
11926     // prebuilt conversion function for this field, instead of
11927     // switching every time we're reading a value
11928     if(!this.convert){
11929         var cv, dateFormat = this.dateFormat;
11930         switch(this.type){
11931             case "":
11932             case "auto":
11933             case undefined:
11934                 cv = function(v){ return v; };
11935                 break;
11936             case "string":
11937                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11938                 break;
11939             case "int":
11940                 cv = function(v){
11941                     return v !== undefined && v !== null && v !== '' ?
11942                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11943                     };
11944                 break;
11945             case "float":
11946                 cv = function(v){
11947                     return v !== undefined && v !== null && v !== '' ?
11948                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11949                     };
11950                 break;
11951             case "bool":
11952             case "boolean":
11953                 cv = function(v){ return v === true || v === "true" || v == 1; };
11954                 break;
11955             case "date":
11956                 cv = function(v){
11957                     if(!v){
11958                         return '';
11959                     }
11960                     if(v instanceof Date){
11961                         return v;
11962                     }
11963                     if(dateFormat){
11964                         if(dateFormat == "timestamp"){
11965                             return new Date(v*1000);
11966                         }
11967                         return Date.parseDate(v, dateFormat);
11968                     }
11969                     var parsed = Date.parse(v);
11970                     return parsed ? new Date(parsed) : null;
11971                 };
11972              break;
11973             
11974         }
11975         this.convert = cv;
11976     }
11977 };
11978
11979 Roo.data.Field.prototype = {
11980     dateFormat: null,
11981     defaultValue: "",
11982     mapping: null,
11983     sortType : null,
11984     sortDir : "ASC"
11985 };/*
11986  * Based on:
11987  * Ext JS Library 1.1.1
11988  * Copyright(c) 2006-2007, Ext JS, LLC.
11989  *
11990  * Originally Released Under LGPL - original licence link has changed is not relivant.
11991  *
11992  * Fork - LGPL
11993  * <script type="text/javascript">
11994  */
11995  
11996 // Base class for reading structured data from a data source.  This class is intended to be
11997 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11998
11999 /**
12000  * @class Roo.data.DataReader
12001  * Base class for reading structured data from a data source.  This class is intended to be
12002  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12003  */
12004
12005 Roo.data.DataReader = function(meta, recordType){
12006     
12007     this.meta = meta;
12008     
12009     this.recordType = recordType instanceof Array ? 
12010         Roo.data.Record.create(recordType) : recordType;
12011 };
12012
12013 Roo.data.DataReader.prototype = {
12014      /**
12015      * Create an empty record
12016      * @param {Object} data (optional) - overlay some values
12017      * @return {Roo.data.Record} record created.
12018      */
12019     newRow :  function(d) {
12020         var da =  {};
12021         this.recordType.prototype.fields.each(function(c) {
12022             switch( c.type) {
12023                 case 'int' : da[c.name] = 0; break;
12024                 case 'date' : da[c.name] = new Date(); break;
12025                 case 'float' : da[c.name] = 0.0; break;
12026                 case 'boolean' : da[c.name] = false; break;
12027                 default : da[c.name] = ""; break;
12028             }
12029             
12030         });
12031         return new this.recordType(Roo.apply(da, d));
12032     }
12033     
12034 };/*
12035  * Based on:
12036  * Ext JS Library 1.1.1
12037  * Copyright(c) 2006-2007, Ext JS, LLC.
12038  *
12039  * Originally Released Under LGPL - original licence link has changed is not relivant.
12040  *
12041  * Fork - LGPL
12042  * <script type="text/javascript">
12043  */
12044
12045 /**
12046  * @class Roo.data.DataProxy
12047  * @extends Roo.data.Observable
12048  * This class is an abstract base class for implementations which provide retrieval of
12049  * unformatted data objects.<br>
12050  * <p>
12051  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12052  * (of the appropriate type which knows how to parse the data object) to provide a block of
12053  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12054  * <p>
12055  * Custom implementations must implement the load method as described in
12056  * {@link Roo.data.HttpProxy#load}.
12057  */
12058 Roo.data.DataProxy = function(){
12059     this.addEvents({
12060         /**
12061          * @event beforeload
12062          * Fires before a network request is made to retrieve a data object.
12063          * @param {Object} This DataProxy object.
12064          * @param {Object} params The params parameter to the load function.
12065          */
12066         beforeload : true,
12067         /**
12068          * @event load
12069          * Fires before the load method's callback is called.
12070          * @param {Object} This DataProxy object.
12071          * @param {Object} o The data object.
12072          * @param {Object} arg The callback argument object passed to the load function.
12073          */
12074         load : true,
12075         /**
12076          * @event loadexception
12077          * Fires if an Exception occurs during data retrieval.
12078          * @param {Object} This DataProxy object.
12079          * @param {Object} o The data object.
12080          * @param {Object} arg The callback argument object passed to the load function.
12081          * @param {Object} e The Exception.
12082          */
12083         loadexception : true
12084     });
12085     Roo.data.DataProxy.superclass.constructor.call(this);
12086 };
12087
12088 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12089
12090     /**
12091      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12092      */
12093 /*
12094  * Based on:
12095  * Ext JS Library 1.1.1
12096  * Copyright(c) 2006-2007, Ext JS, LLC.
12097  *
12098  * Originally Released Under LGPL - original licence link has changed is not relivant.
12099  *
12100  * Fork - LGPL
12101  * <script type="text/javascript">
12102  */
12103 /**
12104  * @class Roo.data.MemoryProxy
12105  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12106  * to the Reader when its load method is called.
12107  * @constructor
12108  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12109  */
12110 Roo.data.MemoryProxy = function(data){
12111     if (data.data) {
12112         data = data.data;
12113     }
12114     Roo.data.MemoryProxy.superclass.constructor.call(this);
12115     this.data = data;
12116 };
12117
12118 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12119     
12120     /**
12121      * Load data from the requested source (in this case an in-memory
12122      * data object passed to the constructor), read the data object into
12123      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12124      * process that block using the passed callback.
12125      * @param {Object} params This parameter is not used by the MemoryProxy class.
12126      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12127      * object into a block of Roo.data.Records.
12128      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12129      * The function must be passed <ul>
12130      * <li>The Record block object</li>
12131      * <li>The "arg" argument from the load function</li>
12132      * <li>A boolean success indicator</li>
12133      * </ul>
12134      * @param {Object} scope The scope in which to call the callback
12135      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12136      */
12137     load : function(params, reader, callback, scope, arg){
12138         params = params || {};
12139         var result;
12140         try {
12141             result = reader.readRecords(this.data);
12142         }catch(e){
12143             this.fireEvent("loadexception", this, arg, null, e);
12144             callback.call(scope, null, arg, false);
12145             return;
12146         }
12147         callback.call(scope, result, arg, true);
12148     },
12149     
12150     // private
12151     update : function(params, records){
12152         
12153     }
12154 });/*
12155  * Based on:
12156  * Ext JS Library 1.1.1
12157  * Copyright(c) 2006-2007, Ext JS, LLC.
12158  *
12159  * Originally Released Under LGPL - original licence link has changed is not relivant.
12160  *
12161  * Fork - LGPL
12162  * <script type="text/javascript">
12163  */
12164 /**
12165  * @class Roo.data.HttpProxy
12166  * @extends Roo.data.DataProxy
12167  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12168  * configured to reference a certain URL.<br><br>
12169  * <p>
12170  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12171  * from which the running page was served.<br><br>
12172  * <p>
12173  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12174  * <p>
12175  * Be aware that to enable the browser to parse an XML document, the server must set
12176  * the Content-Type header in the HTTP response to "text/xml".
12177  * @constructor
12178  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12179  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12180  * will be used to make the request.
12181  */
12182 Roo.data.HttpProxy = function(conn){
12183     Roo.data.HttpProxy.superclass.constructor.call(this);
12184     // is conn a conn config or a real conn?
12185     this.conn = conn;
12186     this.useAjax = !conn || !conn.events;
12187   
12188 };
12189
12190 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12191     // thse are take from connection...
12192     
12193     /**
12194      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12195      */
12196     /**
12197      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12198      * extra parameters to each request made by this object. (defaults to undefined)
12199      */
12200     /**
12201      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12202      *  to each request made by this object. (defaults to undefined)
12203      */
12204     /**
12205      * @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)
12206      */
12207     /**
12208      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12209      */
12210      /**
12211      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12212      * @type Boolean
12213      */
12214   
12215
12216     /**
12217      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12218      * @type Boolean
12219      */
12220     /**
12221      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12222      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12223      * a finer-grained basis than the DataProxy events.
12224      */
12225     getConnection : function(){
12226         return this.useAjax ? Roo.Ajax : this.conn;
12227     },
12228
12229     /**
12230      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12231      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12232      * process that block using the passed callback.
12233      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12234      * for the request to the remote server.
12235      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12236      * object into a block of Roo.data.Records.
12237      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12238      * The function must be passed <ul>
12239      * <li>The Record block object</li>
12240      * <li>The "arg" argument from the load function</li>
12241      * <li>A boolean success indicator</li>
12242      * </ul>
12243      * @param {Object} scope The scope in which to call the callback
12244      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12245      */
12246     load : function(params, reader, callback, scope, arg){
12247         if(this.fireEvent("beforeload", this, params) !== false){
12248             var  o = {
12249                 params : params || {},
12250                 request: {
12251                     callback : callback,
12252                     scope : scope,
12253                     arg : arg
12254                 },
12255                 reader: reader,
12256                 callback : this.loadResponse,
12257                 scope: this
12258             };
12259             if(this.useAjax){
12260                 Roo.applyIf(o, this.conn);
12261                 if(this.activeRequest){
12262                     Roo.Ajax.abort(this.activeRequest);
12263                 }
12264                 this.activeRequest = Roo.Ajax.request(o);
12265             }else{
12266                 this.conn.request(o);
12267             }
12268         }else{
12269             callback.call(scope||this, null, arg, false);
12270         }
12271     },
12272
12273     // private
12274     loadResponse : function(o, success, response){
12275         delete this.activeRequest;
12276         if(!success){
12277             this.fireEvent("loadexception", this, o, response);
12278             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12279             return;
12280         }
12281         var result;
12282         try {
12283             result = o.reader.read(response);
12284         }catch(e){
12285             this.fireEvent("loadexception", this, o, response, e);
12286             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12287             return;
12288         }
12289         
12290         this.fireEvent("load", this, o, o.request.arg);
12291         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12292     },
12293
12294     // private
12295     update : function(dataSet){
12296
12297     },
12298
12299     // private
12300     updateResponse : function(dataSet){
12301
12302     }
12303 });/*
12304  * Based on:
12305  * Ext JS Library 1.1.1
12306  * Copyright(c) 2006-2007, Ext JS, LLC.
12307  *
12308  * Originally Released Under LGPL - original licence link has changed is not relivant.
12309  *
12310  * Fork - LGPL
12311  * <script type="text/javascript">
12312  */
12313
12314 /**
12315  * @class Roo.data.ScriptTagProxy
12316  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12317  * other than the originating domain of the running page.<br><br>
12318  * <p>
12319  * <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
12320  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12321  * <p>
12322  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12323  * source code that is used as the source inside a &lt;script> tag.<br><br>
12324  * <p>
12325  * In order for the browser to process the returned data, the server must wrap the data object
12326  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12327  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12328  * depending on whether the callback name was passed:
12329  * <p>
12330  * <pre><code>
12331 boolean scriptTag = false;
12332 String cb = request.getParameter("callback");
12333 if (cb != null) {
12334     scriptTag = true;
12335     response.setContentType("text/javascript");
12336 } else {
12337     response.setContentType("application/x-json");
12338 }
12339 Writer out = response.getWriter();
12340 if (scriptTag) {
12341     out.write(cb + "(");
12342 }
12343 out.print(dataBlock.toJsonString());
12344 if (scriptTag) {
12345     out.write(");");
12346 }
12347 </pre></code>
12348  *
12349  * @constructor
12350  * @param {Object} config A configuration object.
12351  */
12352 Roo.data.ScriptTagProxy = function(config){
12353     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12354     Roo.apply(this, config);
12355     this.head = document.getElementsByTagName("head")[0];
12356 };
12357
12358 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12359
12360 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12361     /**
12362      * @cfg {String} url The URL from which to request the data object.
12363      */
12364     /**
12365      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12366      */
12367     timeout : 30000,
12368     /**
12369      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12370      * the server the name of the callback function set up by the load call to process the returned data object.
12371      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12372      * javascript output which calls this named function passing the data object as its only parameter.
12373      */
12374     callbackParam : "callback",
12375     /**
12376      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12377      * name to the request.
12378      */
12379     nocache : true,
12380
12381     /**
12382      * Load data from the configured URL, read the data object into
12383      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12384      * process that block using the passed callback.
12385      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12386      * for the request to the remote server.
12387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12388      * object into a block of Roo.data.Records.
12389      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12390      * The function must be passed <ul>
12391      * <li>The Record block object</li>
12392      * <li>The "arg" argument from the load function</li>
12393      * <li>A boolean success indicator</li>
12394      * </ul>
12395      * @param {Object} scope The scope in which to call the callback
12396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12397      */
12398     load : function(params, reader, callback, scope, arg){
12399         if(this.fireEvent("beforeload", this, params) !== false){
12400
12401             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12402
12403             var url = this.url;
12404             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12405             if(this.nocache){
12406                 url += "&_dc=" + (new Date().getTime());
12407             }
12408             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12409             var trans = {
12410                 id : transId,
12411                 cb : "stcCallback"+transId,
12412                 scriptId : "stcScript"+transId,
12413                 params : params,
12414                 arg : arg,
12415                 url : url,
12416                 callback : callback,
12417                 scope : scope,
12418                 reader : reader
12419             };
12420             var conn = this;
12421
12422             window[trans.cb] = function(o){
12423                 conn.handleResponse(o, trans);
12424             };
12425
12426             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12427
12428             if(this.autoAbort !== false){
12429                 this.abort();
12430             }
12431
12432             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12433
12434             var script = document.createElement("script");
12435             script.setAttribute("src", url);
12436             script.setAttribute("type", "text/javascript");
12437             script.setAttribute("id", trans.scriptId);
12438             this.head.appendChild(script);
12439
12440             this.trans = trans;
12441         }else{
12442             callback.call(scope||this, null, arg, false);
12443         }
12444     },
12445
12446     // private
12447     isLoading : function(){
12448         return this.trans ? true : false;
12449     },
12450
12451     /**
12452      * Abort the current server request.
12453      */
12454     abort : function(){
12455         if(this.isLoading()){
12456             this.destroyTrans(this.trans);
12457         }
12458     },
12459
12460     // private
12461     destroyTrans : function(trans, isLoaded){
12462         this.head.removeChild(document.getElementById(trans.scriptId));
12463         clearTimeout(trans.timeoutId);
12464         if(isLoaded){
12465             window[trans.cb] = undefined;
12466             try{
12467                 delete window[trans.cb];
12468             }catch(e){}
12469         }else{
12470             // if hasn't been loaded, wait for load to remove it to prevent script error
12471             window[trans.cb] = function(){
12472                 window[trans.cb] = undefined;
12473                 try{
12474                     delete window[trans.cb];
12475                 }catch(e){}
12476             };
12477         }
12478     },
12479
12480     // private
12481     handleResponse : function(o, trans){
12482         this.trans = false;
12483         this.destroyTrans(trans, true);
12484         var result;
12485         try {
12486             result = trans.reader.readRecords(o);
12487         }catch(e){
12488             this.fireEvent("loadexception", this, o, trans.arg, e);
12489             trans.callback.call(trans.scope||window, null, trans.arg, false);
12490             return;
12491         }
12492         this.fireEvent("load", this, o, trans.arg);
12493         trans.callback.call(trans.scope||window, result, trans.arg, true);
12494     },
12495
12496     // private
12497     handleFailure : function(trans){
12498         this.trans = false;
12499         this.destroyTrans(trans, false);
12500         this.fireEvent("loadexception", this, null, trans.arg);
12501         trans.callback.call(trans.scope||window, null, trans.arg, false);
12502     }
12503 });/*
12504  * Based on:
12505  * Ext JS Library 1.1.1
12506  * Copyright(c) 2006-2007, Ext JS, LLC.
12507  *
12508  * Originally Released Under LGPL - original licence link has changed is not relivant.
12509  *
12510  * Fork - LGPL
12511  * <script type="text/javascript">
12512  */
12513
12514 /**
12515  * @class Roo.data.JsonReader
12516  * @extends Roo.data.DataReader
12517  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12518  * based on mappings in a provided Roo.data.Record constructor.
12519  * 
12520  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12521  * in the reply previously. 
12522  * 
12523  * <p>
12524  * Example code:
12525  * <pre><code>
12526 var RecordDef = Roo.data.Record.create([
12527     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12528     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12529 ]);
12530 var myReader = new Roo.data.JsonReader({
12531     totalProperty: "results",    // The property which contains the total dataset size (optional)
12532     root: "rows",                // The property which contains an Array of row objects
12533     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12534 }, RecordDef);
12535 </code></pre>
12536  * <p>
12537  * This would consume a JSON file like this:
12538  * <pre><code>
12539 { 'results': 2, 'rows': [
12540     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12541     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12542 }
12543 </code></pre>
12544  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12545  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12546  * paged from the remote server.
12547  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12548  * @cfg {String} root name of the property which contains the Array of row objects.
12549  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12550  * @cfg {Array} fields Array of field definition objects
12551  * @constructor
12552  * Create a new JsonReader
12553  * @param {Object} meta Metadata configuration options
12554  * @param {Object} recordType Either an Array of field definition objects,
12555  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12556  */
12557 Roo.data.JsonReader = function(meta, recordType){
12558     
12559     meta = meta || {};
12560     // set some defaults:
12561     Roo.applyIf(meta, {
12562         totalProperty: 'total',
12563         successProperty : 'success',
12564         root : 'data',
12565         id : 'id'
12566     });
12567     
12568     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12569 };
12570 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12571     
12572     /**
12573      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12574      * Used by Store query builder to append _requestMeta to params.
12575      * 
12576      */
12577     metaFromRemote : false,
12578     /**
12579      * This method is only used by a DataProxy which has retrieved data from a remote server.
12580      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12581      * @return {Object} data A data block which is used by an Roo.data.Store object as
12582      * a cache of Roo.data.Records.
12583      */
12584     read : function(response){
12585         var json = response.responseText;
12586        
12587         var o = /* eval:var:o */ eval("("+json+")");
12588         if(!o) {
12589             throw {message: "JsonReader.read: Json object not found"};
12590         }
12591         
12592         if(o.metaData){
12593             
12594             delete this.ef;
12595             this.metaFromRemote = true;
12596             this.meta = o.metaData;
12597             this.recordType = Roo.data.Record.create(o.metaData.fields);
12598             this.onMetaChange(this.meta, this.recordType, o);
12599         }
12600         return this.readRecords(o);
12601     },
12602
12603     // private function a store will implement
12604     onMetaChange : function(meta, recordType, o){
12605
12606     },
12607
12608     /**
12609          * @ignore
12610          */
12611     simpleAccess: function(obj, subsc) {
12612         return obj[subsc];
12613     },
12614
12615         /**
12616          * @ignore
12617          */
12618     getJsonAccessor: function(){
12619         var re = /[\[\.]/;
12620         return function(expr) {
12621             try {
12622                 return(re.test(expr))
12623                     ? new Function("obj", "return obj." + expr)
12624                     : function(obj){
12625                         return obj[expr];
12626                     };
12627             } catch(e){}
12628             return Roo.emptyFn;
12629         };
12630     }(),
12631
12632     /**
12633      * Create a data block containing Roo.data.Records from an XML document.
12634      * @param {Object} o An object which contains an Array of row objects in the property specified
12635      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12636      * which contains the total size of the dataset.
12637      * @return {Object} data A data block which is used by an Roo.data.Store object as
12638      * a cache of Roo.data.Records.
12639      */
12640     readRecords : function(o){
12641         /**
12642          * After any data loads, the raw JSON data is available for further custom processing.
12643          * @type Object
12644          */
12645         this.o = o;
12646         var s = this.meta, Record = this.recordType,
12647             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12648
12649 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12650         if (!this.ef) {
12651             if(s.totalProperty) {
12652                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12653                 }
12654                 if(s.successProperty) {
12655                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12656                 }
12657                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12658                 if (s.id) {
12659                         var g = this.getJsonAccessor(s.id);
12660                         this.getId = function(rec) {
12661                                 var r = g(rec);  
12662                                 return (r === undefined || r === "") ? null : r;
12663                         };
12664                 } else {
12665                         this.getId = function(){return null;};
12666                 }
12667             this.ef = [];
12668             for(var jj = 0; jj < fl; jj++){
12669                 f = fi[jj];
12670                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12671                 this.ef[jj] = this.getJsonAccessor(map);
12672             }
12673         }
12674
12675         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12676         if(s.totalProperty){
12677             var vt = parseInt(this.getTotal(o), 10);
12678             if(!isNaN(vt)){
12679                 totalRecords = vt;
12680             }
12681         }
12682         if(s.successProperty){
12683             var vs = this.getSuccess(o);
12684             if(vs === false || vs === 'false'){
12685                 success = false;
12686             }
12687         }
12688         var records = [];
12689         for(var i = 0; i < c; i++){
12690                 var n = root[i];
12691             var values = {};
12692             var id = this.getId(n);
12693             for(var j = 0; j < fl; j++){
12694                 f = fi[j];
12695             var v = this.ef[j](n);
12696             if (!f.convert) {
12697                 Roo.log('missing convert for ' + f.name);
12698                 Roo.log(f);
12699                 continue;
12700             }
12701             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12702             }
12703             var record = new Record(values, id);
12704             record.json = n;
12705             records[i] = record;
12706         }
12707         return {
12708             raw : o,
12709             success : success,
12710             records : records,
12711             totalRecords : totalRecords
12712         };
12713     }
12714 });/*
12715  * Based on:
12716  * Ext JS Library 1.1.1
12717  * Copyright(c) 2006-2007, Ext JS, LLC.
12718  *
12719  * Originally Released Under LGPL - original licence link has changed is not relivant.
12720  *
12721  * Fork - LGPL
12722  * <script type="text/javascript">
12723  */
12724
12725 /**
12726  * @class Roo.data.ArrayReader
12727  * @extends Roo.data.DataReader
12728  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12729  * Each element of that Array represents a row of data fields. The
12730  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12731  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12732  * <p>
12733  * Example code:.
12734  * <pre><code>
12735 var RecordDef = Roo.data.Record.create([
12736     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12737     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12738 ]);
12739 var myReader = new Roo.data.ArrayReader({
12740     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12741 }, RecordDef);
12742 </code></pre>
12743  * <p>
12744  * This would consume an Array like this:
12745  * <pre><code>
12746 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12747   </code></pre>
12748  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12749  * @constructor
12750  * Create a new JsonReader
12751  * @param {Object} meta Metadata configuration options.
12752  * @param {Object} recordType Either an Array of field definition objects
12753  * as specified to {@link Roo.data.Record#create},
12754  * or an {@link Roo.data.Record} object
12755  * created using {@link Roo.data.Record#create}.
12756  */
12757 Roo.data.ArrayReader = function(meta, recordType){
12758     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12759 };
12760
12761 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12762     /**
12763      * Create a data block containing Roo.data.Records from an XML document.
12764      * @param {Object} o An Array of row objects which represents the dataset.
12765      * @return {Object} data A data block which is used by an Roo.data.Store object as
12766      * a cache of Roo.data.Records.
12767      */
12768     readRecords : function(o){
12769         var sid = this.meta ? this.meta.id : null;
12770         var recordType = this.recordType, fields = recordType.prototype.fields;
12771         var records = [];
12772         var root = o;
12773             for(var i = 0; i < root.length; i++){
12774                     var n = root[i];
12775                 var values = {};
12776                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12777                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12778                 var f = fields.items[j];
12779                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12780                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12781                 v = f.convert(v);
12782                 values[f.name] = v;
12783             }
12784                 var record = new recordType(values, id);
12785                 record.json = n;
12786                 records[records.length] = record;
12787             }
12788             return {
12789                 records : records,
12790                 totalRecords : records.length
12791             };
12792     }
12793 });/*
12794  * - LGPL
12795  * * 
12796  */
12797
12798 /**
12799  * @class Roo.bootstrap.ComboBox
12800  * @extends Roo.bootstrap.TriggerField
12801  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12802  * @cfg {Boolean} append (true|false) default false
12803  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12804  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12805  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12806  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12807  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12808  * @cfg {Boolean} animate default true
12809  * @cfg {Boolean} emptyResultText only for touch device
12810  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12811  * @cfg {String} emptyTitle default ''
12812  * @constructor
12813  * Create a new ComboBox.
12814  * @param {Object} config Configuration options
12815  */
12816 Roo.bootstrap.ComboBox = function(config){
12817     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12818     this.addEvents({
12819         /**
12820          * @event expand
12821          * Fires when the dropdown list is expanded
12822         * @param {Roo.bootstrap.ComboBox} combo This combo box
12823         */
12824         'expand' : true,
12825         /**
12826          * @event collapse
12827          * Fires when the dropdown list is collapsed
12828         * @param {Roo.bootstrap.ComboBox} combo This combo box
12829         */
12830         'collapse' : true,
12831         /**
12832          * @event beforeselect
12833          * Fires before a list item is selected. Return false to cancel the selection.
12834         * @param {Roo.bootstrap.ComboBox} combo This combo box
12835         * @param {Roo.data.Record} record The data record returned from the underlying store
12836         * @param {Number} index The index of the selected item in the dropdown list
12837         */
12838         'beforeselect' : true,
12839         /**
12840          * @event select
12841          * Fires when a list item is selected
12842         * @param {Roo.bootstrap.ComboBox} combo This combo box
12843         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12844         * @param {Number} index The index of the selected item in the dropdown list
12845         */
12846         'select' : true,
12847         /**
12848          * @event beforequery
12849          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12850          * The event object passed has these properties:
12851         * @param {Roo.bootstrap.ComboBox} combo This combo box
12852         * @param {String} query The query
12853         * @param {Boolean} forceAll true to force "all" query
12854         * @param {Boolean} cancel true to cancel the query
12855         * @param {Object} e The query event object
12856         */
12857         'beforequery': true,
12858          /**
12859          * @event add
12860          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12861         * @param {Roo.bootstrap.ComboBox} combo This combo box
12862         */
12863         'add' : true,
12864         /**
12865          * @event edit
12866          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12867         * @param {Roo.bootstrap.ComboBox} combo This combo box
12868         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12869         */
12870         'edit' : true,
12871         /**
12872          * @event remove
12873          * Fires when the remove value from the combobox array
12874         * @param {Roo.bootstrap.ComboBox} combo This combo box
12875         */
12876         'remove' : true,
12877         /**
12878          * @event afterremove
12879          * Fires when the remove value from the combobox array
12880         * @param {Roo.bootstrap.ComboBox} combo This combo box
12881         */
12882         'afterremove' : true,
12883         /**
12884          * @event specialfilter
12885          * Fires when specialfilter
12886             * @param {Roo.bootstrap.ComboBox} combo This combo box
12887             */
12888         'specialfilter' : true,
12889         /**
12890          * @event tick
12891          * Fires when tick the element
12892             * @param {Roo.bootstrap.ComboBox} combo This combo box
12893             */
12894         'tick' : true,
12895         /**
12896          * @event touchviewdisplay
12897          * Fires when touch view require special display (default is using displayField)
12898             * @param {Roo.bootstrap.ComboBox} combo This combo box
12899             * @param {Object} cfg set html .
12900             */
12901         'touchviewdisplay' : true
12902         
12903     });
12904     
12905     this.item = [];
12906     this.tickItems = [];
12907     
12908     this.selectedIndex = -1;
12909     if(this.mode == 'local'){
12910         if(config.queryDelay === undefined){
12911             this.queryDelay = 10;
12912         }
12913         if(config.minChars === undefined){
12914             this.minChars = 0;
12915         }
12916     }
12917 };
12918
12919 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12920      
12921     /**
12922      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12923      * rendering into an Roo.Editor, defaults to false)
12924      */
12925     /**
12926      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12927      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12928      */
12929     /**
12930      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12931      */
12932     /**
12933      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12934      * the dropdown list (defaults to undefined, with no header element)
12935      */
12936
12937      /**
12938      * @cfg {String/Roo.Template} tpl The template to use to render the output
12939      */
12940      
12941      /**
12942      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12943      */
12944     listWidth: undefined,
12945     /**
12946      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12947      * mode = 'remote' or 'text' if mode = 'local')
12948      */
12949     displayField: undefined,
12950     
12951     /**
12952      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12953      * mode = 'remote' or 'value' if mode = 'local'). 
12954      * Note: use of a valueField requires the user make a selection
12955      * in order for a value to be mapped.
12956      */
12957     valueField: undefined,
12958     /**
12959      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12960      */
12961     modalTitle : '',
12962     
12963     /**
12964      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12965      * field's data value (defaults to the underlying DOM element's name)
12966      */
12967     hiddenName: undefined,
12968     /**
12969      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12970      */
12971     listClass: '',
12972     /**
12973      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12974      */
12975     selectedClass: 'active',
12976     
12977     /**
12978      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12979      */
12980     shadow:'sides',
12981     /**
12982      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12983      * anchor positions (defaults to 'tl-bl')
12984      */
12985     listAlign: 'tl-bl?',
12986     /**
12987      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12988      */
12989     maxHeight: 300,
12990     /**
12991      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12992      * query specified by the allQuery config option (defaults to 'query')
12993      */
12994     triggerAction: 'query',
12995     /**
12996      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12997      * (defaults to 4, does not apply if editable = false)
12998      */
12999     minChars : 4,
13000     /**
13001      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13002      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13003      */
13004     typeAhead: false,
13005     /**
13006      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13007      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13008      */
13009     queryDelay: 500,
13010     /**
13011      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13012      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13013      */
13014     pageSize: 0,
13015     /**
13016      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13017      * when editable = true (defaults to false)
13018      */
13019     selectOnFocus:false,
13020     /**
13021      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13022      */
13023     queryParam: 'query',
13024     /**
13025      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13026      * when mode = 'remote' (defaults to 'Loading...')
13027      */
13028     loadingText: 'Loading...',
13029     /**
13030      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13031      */
13032     resizable: false,
13033     /**
13034      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13035      */
13036     handleHeight : 8,
13037     /**
13038      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13039      * traditional select (defaults to true)
13040      */
13041     editable: true,
13042     /**
13043      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13044      */
13045     allQuery: '',
13046     /**
13047      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13048      */
13049     mode: 'remote',
13050     /**
13051      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13052      * listWidth has a higher value)
13053      */
13054     minListWidth : 70,
13055     /**
13056      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13057      * allow the user to set arbitrary text into the field (defaults to false)
13058      */
13059     forceSelection:false,
13060     /**
13061      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13062      * if typeAhead = true (defaults to 250)
13063      */
13064     typeAheadDelay : 250,
13065     /**
13066      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13067      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13068      */
13069     valueNotFoundText : undefined,
13070     /**
13071      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13072      */
13073     blockFocus : false,
13074     
13075     /**
13076      * @cfg {Boolean} disableClear Disable showing of clear button.
13077      */
13078     disableClear : false,
13079     /**
13080      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13081      */
13082     alwaysQuery : false,
13083     
13084     /**
13085      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13086      */
13087     multiple : false,
13088     
13089     /**
13090      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13091      */
13092     invalidClass : "has-warning",
13093     
13094     /**
13095      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13096      */
13097     validClass : "has-success",
13098     
13099     /**
13100      * @cfg {Boolean} specialFilter (true|false) special filter default false
13101      */
13102     specialFilter : false,
13103     
13104     /**
13105      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13106      */
13107     mobileTouchView : true,
13108     
13109     /**
13110      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13111      */
13112     useNativeIOS : false,
13113     
13114     /**
13115      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13116      */
13117     mobile_restrict_height : false,
13118     
13119     ios_options : false,
13120     
13121     //private
13122     addicon : false,
13123     editicon: false,
13124     
13125     page: 0,
13126     hasQuery: false,
13127     append: false,
13128     loadNext: false,
13129     autoFocus : true,
13130     tickable : false,
13131     btnPosition : 'right',
13132     triggerList : true,
13133     showToggleBtn : true,
13134     animate : true,
13135     emptyResultText: 'Empty',
13136     triggerText : 'Select',
13137     emptyTitle : '',
13138     
13139     // element that contains real text value.. (when hidden is used..)
13140     
13141     getAutoCreate : function()
13142     {   
13143         var cfg = false;
13144         //render
13145         /*
13146          * Render classic select for iso
13147          */
13148         
13149         if(Roo.isIOS && this.useNativeIOS){
13150             cfg = this.getAutoCreateNativeIOS();
13151             return cfg;
13152         }
13153         
13154         /*
13155          * Touch Devices
13156          */
13157         
13158         if(Roo.isTouch && this.mobileTouchView){
13159             cfg = this.getAutoCreateTouchView();
13160             return cfg;;
13161         }
13162         
13163         /*
13164          *  Normal ComboBox
13165          */
13166         if(!this.tickable){
13167             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13168             return cfg;
13169         }
13170         
13171         /*
13172          *  ComboBox with tickable selections
13173          */
13174              
13175         var align = this.labelAlign || this.parentLabelAlign();
13176         
13177         cfg = {
13178             cls : 'form-group roo-combobox-tickable' //input-group
13179         };
13180         
13181         var btn_text_select = '';
13182         var btn_text_done = '';
13183         var btn_text_cancel = '';
13184         
13185         if (this.btn_text_show) {
13186             btn_text_select = 'Select';
13187             btn_text_done = 'Done';
13188             btn_text_cancel = 'Cancel'; 
13189         }
13190         
13191         var buttons = {
13192             tag : 'div',
13193             cls : 'tickable-buttons',
13194             cn : [
13195                 {
13196                     tag : 'button',
13197                     type : 'button',
13198                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13199                     //html : this.triggerText
13200                     html: btn_text_select
13201                 },
13202                 {
13203                     tag : 'button',
13204                     type : 'button',
13205                     name : 'ok',
13206                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13207                     //html : 'Done'
13208                     html: btn_text_done
13209                 },
13210                 {
13211                     tag : 'button',
13212                     type : 'button',
13213                     name : 'cancel',
13214                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13215                     //html : 'Cancel'
13216                     html: btn_text_cancel
13217                 }
13218             ]
13219         };
13220         
13221         if(this.editable){
13222             buttons.cn.unshift({
13223                 tag: 'input',
13224                 cls: 'roo-select2-search-field-input'
13225             });
13226         }
13227         
13228         var _this = this;
13229         
13230         Roo.each(buttons.cn, function(c){
13231             if (_this.size) {
13232                 c.cls += ' btn-' + _this.size;
13233             }
13234
13235             if (_this.disabled) {
13236                 c.disabled = true;
13237             }
13238         });
13239         
13240         var box = {
13241             tag: 'div',
13242             cn: [
13243                 {
13244                     tag: 'input',
13245                     type : 'hidden',
13246                     cls: 'form-hidden-field'
13247                 },
13248                 {
13249                     tag: 'ul',
13250                     cls: 'roo-select2-choices',
13251                     cn:[
13252                         {
13253                             tag: 'li',
13254                             cls: 'roo-select2-search-field',
13255                             cn: [
13256                                 buttons
13257                             ]
13258                         }
13259                     ]
13260                 }
13261             ]
13262         };
13263         
13264         var combobox = {
13265             cls: 'roo-select2-container input-group roo-select2-container-multi',
13266             cn: [
13267                 box
13268 //                {
13269 //                    tag: 'ul',
13270 //                    cls: 'typeahead typeahead-long dropdown-menu',
13271 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13272 //                }
13273             ]
13274         };
13275         
13276         if(this.hasFeedback && !this.allowBlank){
13277             
13278             var feedback = {
13279                 tag: 'span',
13280                 cls: 'glyphicon form-control-feedback'
13281             };
13282
13283             combobox.cn.push(feedback);
13284         }
13285         
13286         
13287         if (align ==='left' && this.fieldLabel.length) {
13288             
13289             cfg.cls += ' roo-form-group-label-left';
13290             
13291             cfg.cn = [
13292                 {
13293                     tag : 'i',
13294                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13295                     tooltip : 'This field is required'
13296                 },
13297                 {
13298                     tag: 'label',
13299                     'for' :  id,
13300                     cls : 'control-label',
13301                     html : this.fieldLabel
13302
13303                 },
13304                 {
13305                     cls : "", 
13306                     cn: [
13307                         combobox
13308                     ]
13309                 }
13310
13311             ];
13312             
13313             var labelCfg = cfg.cn[1];
13314             var contentCfg = cfg.cn[2];
13315             
13316
13317             if(this.indicatorpos == 'right'){
13318                 
13319                 cfg.cn = [
13320                     {
13321                         tag: 'label',
13322                         'for' :  id,
13323                         cls : 'control-label',
13324                         cn : [
13325                             {
13326                                 tag : 'span',
13327                                 html : this.fieldLabel
13328                             },
13329                             {
13330                                 tag : 'i',
13331                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13332                                 tooltip : 'This field is required'
13333                             }
13334                         ]
13335                     },
13336                     {
13337                         cls : "",
13338                         cn: [
13339                             combobox
13340                         ]
13341                     }
13342
13343                 ];
13344                 
13345                 
13346                 
13347                 labelCfg = cfg.cn[0];
13348                 contentCfg = cfg.cn[1];
13349             
13350             }
13351             
13352             if(this.labelWidth > 12){
13353                 labelCfg.style = "width: " + this.labelWidth + 'px';
13354             }
13355             
13356             if(this.labelWidth < 13 && this.labelmd == 0){
13357                 this.labelmd = this.labelWidth;
13358             }
13359             
13360             if(this.labellg > 0){
13361                 labelCfg.cls += ' col-lg-' + this.labellg;
13362                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13363             }
13364             
13365             if(this.labelmd > 0){
13366                 labelCfg.cls += ' col-md-' + this.labelmd;
13367                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13368             }
13369             
13370             if(this.labelsm > 0){
13371                 labelCfg.cls += ' col-sm-' + this.labelsm;
13372                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13373             }
13374             
13375             if(this.labelxs > 0){
13376                 labelCfg.cls += ' col-xs-' + this.labelxs;
13377                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13378             }
13379                 
13380                 
13381         } else if ( this.fieldLabel.length) {
13382 //                Roo.log(" label");
13383                  cfg.cn = [
13384                     {
13385                         tag : 'i',
13386                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13387                         tooltip : 'This field is required'
13388                     },
13389                     {
13390                         tag: 'label',
13391                         //cls : 'input-group-addon',
13392                         html : this.fieldLabel
13393                     },
13394                     combobox
13395                 ];
13396                 
13397                 if(this.indicatorpos == 'right'){
13398                     cfg.cn = [
13399                         {
13400                             tag: 'label',
13401                             //cls : 'input-group-addon',
13402                             html : this.fieldLabel
13403                         },
13404                         {
13405                             tag : 'i',
13406                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13407                             tooltip : 'This field is required'
13408                         },
13409                         combobox
13410                     ];
13411                     
13412                 }
13413
13414         } else {
13415             
13416 //                Roo.log(" no label && no align");
13417                 cfg = combobox
13418                      
13419                 
13420         }
13421          
13422         var settings=this;
13423         ['xs','sm','md','lg'].map(function(size){
13424             if (settings[size]) {
13425                 cfg.cls += ' col-' + size + '-' + settings[size];
13426             }
13427         });
13428         
13429         return cfg;
13430         
13431     },
13432     
13433     _initEventsCalled : false,
13434     
13435     // private
13436     initEvents: function()
13437     {   
13438         if (this._initEventsCalled) { // as we call render... prevent looping...
13439             return;
13440         }
13441         this._initEventsCalled = true;
13442         
13443         if (!this.store) {
13444             throw "can not find store for combo";
13445         }
13446         
13447         this.indicator = this.indicatorEl();
13448         
13449         this.store = Roo.factory(this.store, Roo.data);
13450         this.store.parent = this;
13451         
13452         // if we are building from html. then this element is so complex, that we can not really
13453         // use the rendered HTML.
13454         // so we have to trash and replace the previous code.
13455         if (Roo.XComponent.build_from_html) {
13456             // remove this element....
13457             var e = this.el.dom, k=0;
13458             while (e ) { e = e.previousSibling;  ++k;}
13459
13460             this.el.remove();
13461             
13462             this.el=false;
13463             this.rendered = false;
13464             
13465             this.render(this.parent().getChildContainer(true), k);
13466         }
13467         
13468         if(Roo.isIOS && this.useNativeIOS){
13469             this.initIOSView();
13470             return;
13471         }
13472         
13473         /*
13474          * Touch Devices
13475          */
13476         
13477         if(Roo.isTouch && this.mobileTouchView){
13478             this.initTouchView();
13479             return;
13480         }
13481         
13482         if(this.tickable){
13483             this.initTickableEvents();
13484             return;
13485         }
13486         
13487         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13488         
13489         if(this.hiddenName){
13490             
13491             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13492             
13493             this.hiddenField.dom.value =
13494                 this.hiddenValue !== undefined ? this.hiddenValue :
13495                 this.value !== undefined ? this.value : '';
13496
13497             // prevent input submission
13498             this.el.dom.removeAttribute('name');
13499             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13500              
13501              
13502         }
13503         //if(Roo.isGecko){
13504         //    this.el.dom.setAttribute('autocomplete', 'off');
13505         //}
13506         
13507         var cls = 'x-combo-list';
13508         
13509         //this.list = new Roo.Layer({
13510         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13511         //});
13512         
13513         var _this = this;
13514         
13515         (function(){
13516             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13517             _this.list.setWidth(lw);
13518         }).defer(100);
13519         
13520         this.list.on('mouseover', this.onViewOver, this);
13521         this.list.on('mousemove', this.onViewMove, this);
13522         this.list.on('scroll', this.onViewScroll, this);
13523         
13524         /*
13525         this.list.swallowEvent('mousewheel');
13526         this.assetHeight = 0;
13527
13528         if(this.title){
13529             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13530             this.assetHeight += this.header.getHeight();
13531         }
13532
13533         this.innerList = this.list.createChild({cls:cls+'-inner'});
13534         this.innerList.on('mouseover', this.onViewOver, this);
13535         this.innerList.on('mousemove', this.onViewMove, this);
13536         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13537         
13538         if(this.allowBlank && !this.pageSize && !this.disableClear){
13539             this.footer = this.list.createChild({cls:cls+'-ft'});
13540             this.pageTb = new Roo.Toolbar(this.footer);
13541            
13542         }
13543         if(this.pageSize){
13544             this.footer = this.list.createChild({cls:cls+'-ft'});
13545             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13546                     {pageSize: this.pageSize});
13547             
13548         }
13549         
13550         if (this.pageTb && this.allowBlank && !this.disableClear) {
13551             var _this = this;
13552             this.pageTb.add(new Roo.Toolbar.Fill(), {
13553                 cls: 'x-btn-icon x-btn-clear',
13554                 text: '&#160;',
13555                 handler: function()
13556                 {
13557                     _this.collapse();
13558                     _this.clearValue();
13559                     _this.onSelect(false, -1);
13560                 }
13561             });
13562         }
13563         if (this.footer) {
13564             this.assetHeight += this.footer.getHeight();
13565         }
13566         */
13567             
13568         if(!this.tpl){
13569             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13570         }
13571
13572         this.view = new Roo.View(this.list, this.tpl, {
13573             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13574         });
13575         //this.view.wrapEl.setDisplayed(false);
13576         this.view.on('click', this.onViewClick, this);
13577         
13578         
13579         this.store.on('beforeload', this.onBeforeLoad, this);
13580         this.store.on('load', this.onLoad, this);
13581         this.store.on('loadexception', this.onLoadException, this);
13582         /*
13583         if(this.resizable){
13584             this.resizer = new Roo.Resizable(this.list,  {
13585                pinned:true, handles:'se'
13586             });
13587             this.resizer.on('resize', function(r, w, h){
13588                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13589                 this.listWidth = w;
13590                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13591                 this.restrictHeight();
13592             }, this);
13593             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13594         }
13595         */
13596         if(!this.editable){
13597             this.editable = true;
13598             this.setEditable(false);
13599         }
13600         
13601         /*
13602         
13603         if (typeof(this.events.add.listeners) != 'undefined') {
13604             
13605             this.addicon = this.wrap.createChild(
13606                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13607        
13608             this.addicon.on('click', function(e) {
13609                 this.fireEvent('add', this);
13610             }, this);
13611         }
13612         if (typeof(this.events.edit.listeners) != 'undefined') {
13613             
13614             this.editicon = this.wrap.createChild(
13615                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13616             if (this.addicon) {
13617                 this.editicon.setStyle('margin-left', '40px');
13618             }
13619             this.editicon.on('click', function(e) {
13620                 
13621                 // we fire even  if inothing is selected..
13622                 this.fireEvent('edit', this, this.lastData );
13623                 
13624             }, this);
13625         }
13626         */
13627         
13628         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13629             "up" : function(e){
13630                 this.inKeyMode = true;
13631                 this.selectPrev();
13632             },
13633
13634             "down" : function(e){
13635                 if(!this.isExpanded()){
13636                     this.onTriggerClick();
13637                 }else{
13638                     this.inKeyMode = true;
13639                     this.selectNext();
13640                 }
13641             },
13642
13643             "enter" : function(e){
13644 //                this.onViewClick();
13645                 //return true;
13646                 this.collapse();
13647                 
13648                 if(this.fireEvent("specialkey", this, e)){
13649                     this.onViewClick(false);
13650                 }
13651                 
13652                 return true;
13653             },
13654
13655             "esc" : function(e){
13656                 this.collapse();
13657             },
13658
13659             "tab" : function(e){
13660                 this.collapse();
13661                 
13662                 if(this.fireEvent("specialkey", this, e)){
13663                     this.onViewClick(false);
13664                 }
13665                 
13666                 return true;
13667             },
13668
13669             scope : this,
13670
13671             doRelay : function(foo, bar, hname){
13672                 if(hname == 'down' || this.scope.isExpanded()){
13673                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13674                 }
13675                 return true;
13676             },
13677
13678             forceKeyDown: true
13679         });
13680         
13681         
13682         this.queryDelay = Math.max(this.queryDelay || 10,
13683                 this.mode == 'local' ? 10 : 250);
13684         
13685         
13686         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13687         
13688         if(this.typeAhead){
13689             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13690         }
13691         if(this.editable !== false){
13692             this.inputEl().on("keyup", this.onKeyUp, this);
13693         }
13694         if(this.forceSelection){
13695             this.inputEl().on('blur', this.doForce, this);
13696         }
13697         
13698         if(this.multiple){
13699             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13700             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13701         }
13702     },
13703     
13704     initTickableEvents: function()
13705     {   
13706         this.createList();
13707         
13708         if(this.hiddenName){
13709             
13710             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13711             
13712             this.hiddenField.dom.value =
13713                 this.hiddenValue !== undefined ? this.hiddenValue :
13714                 this.value !== undefined ? this.value : '';
13715
13716             // prevent input submission
13717             this.el.dom.removeAttribute('name');
13718             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13719              
13720              
13721         }
13722         
13723 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13724         
13725         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13726         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13727         if(this.triggerList){
13728             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13729         }
13730          
13731         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13732         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13733         
13734         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13735         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13736         
13737         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13738         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13739         
13740         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13741         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13742         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13743         
13744         this.okBtn.hide();
13745         this.cancelBtn.hide();
13746         
13747         var _this = this;
13748         
13749         (function(){
13750             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13751             _this.list.setWidth(lw);
13752         }).defer(100);
13753         
13754         this.list.on('mouseover', this.onViewOver, this);
13755         this.list.on('mousemove', this.onViewMove, this);
13756         
13757         this.list.on('scroll', this.onViewScroll, this);
13758         
13759         if(!this.tpl){
13760             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13761                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13762         }
13763
13764         this.view = new Roo.View(this.list, this.tpl, {
13765             singleSelect:true,
13766             tickable:true,
13767             parent:this,
13768             store: this.store,
13769             selectedClass: this.selectedClass
13770         });
13771         
13772         //this.view.wrapEl.setDisplayed(false);
13773         this.view.on('click', this.onViewClick, this);
13774         
13775         
13776         
13777         this.store.on('beforeload', this.onBeforeLoad, this);
13778         this.store.on('load', this.onLoad, this);
13779         this.store.on('loadexception', this.onLoadException, this);
13780         
13781         if(this.editable){
13782             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13783                 "up" : function(e){
13784                     this.inKeyMode = true;
13785                     this.selectPrev();
13786                 },
13787
13788                 "down" : function(e){
13789                     this.inKeyMode = true;
13790                     this.selectNext();
13791                 },
13792
13793                 "enter" : function(e){
13794                     if(this.fireEvent("specialkey", this, e)){
13795                         this.onViewClick(false);
13796                     }
13797                     
13798                     return true;
13799                 },
13800
13801                 "esc" : function(e){
13802                     this.onTickableFooterButtonClick(e, false, false);
13803                 },
13804
13805                 "tab" : function(e){
13806                     this.fireEvent("specialkey", this, e);
13807                     
13808                     this.onTickableFooterButtonClick(e, false, false);
13809                     
13810                     return true;
13811                 },
13812
13813                 scope : this,
13814
13815                 doRelay : function(e, fn, key){
13816                     if(this.scope.isExpanded()){
13817                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13818                     }
13819                     return true;
13820                 },
13821
13822                 forceKeyDown: true
13823             });
13824         }
13825         
13826         this.queryDelay = Math.max(this.queryDelay || 10,
13827                 this.mode == 'local' ? 10 : 250);
13828         
13829         
13830         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13831         
13832         if(this.typeAhead){
13833             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13834         }
13835         
13836         if(this.editable !== false){
13837             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13838         }
13839         
13840         this.indicator = this.indicatorEl();
13841         
13842         if(this.indicator){
13843             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13844             this.indicator.hide();
13845         }
13846         
13847     },
13848
13849     onDestroy : function(){
13850         if(this.view){
13851             this.view.setStore(null);
13852             this.view.el.removeAllListeners();
13853             this.view.el.remove();
13854             this.view.purgeListeners();
13855         }
13856         if(this.list){
13857             this.list.dom.innerHTML  = '';
13858         }
13859         
13860         if(this.store){
13861             this.store.un('beforeload', this.onBeforeLoad, this);
13862             this.store.un('load', this.onLoad, this);
13863             this.store.un('loadexception', this.onLoadException, this);
13864         }
13865         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13866     },
13867
13868     // private
13869     fireKey : function(e){
13870         if(e.isNavKeyPress() && !this.list.isVisible()){
13871             this.fireEvent("specialkey", this, e);
13872         }
13873     },
13874
13875     // private
13876     onResize: function(w, h){
13877 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13878 //        
13879 //        if(typeof w != 'number'){
13880 //            // we do not handle it!?!?
13881 //            return;
13882 //        }
13883 //        var tw = this.trigger.getWidth();
13884 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13885 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13886 //        var x = w - tw;
13887 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13888 //            
13889 //        //this.trigger.setStyle('left', x+'px');
13890 //        
13891 //        if(this.list && this.listWidth === undefined){
13892 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13893 //            this.list.setWidth(lw);
13894 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13895 //        }
13896         
13897     
13898         
13899     },
13900
13901     /**
13902      * Allow or prevent the user from directly editing the field text.  If false is passed,
13903      * the user will only be able to select from the items defined in the dropdown list.  This method
13904      * is the runtime equivalent of setting the 'editable' config option at config time.
13905      * @param {Boolean} value True to allow the user to directly edit the field text
13906      */
13907     setEditable : function(value){
13908         if(value == this.editable){
13909             return;
13910         }
13911         this.editable = value;
13912         if(!value){
13913             this.inputEl().dom.setAttribute('readOnly', true);
13914             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13915             this.inputEl().addClass('x-combo-noedit');
13916         }else{
13917             this.inputEl().dom.setAttribute('readOnly', false);
13918             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13919             this.inputEl().removeClass('x-combo-noedit');
13920         }
13921     },
13922
13923     // private
13924     
13925     onBeforeLoad : function(combo,opts){
13926         if(!this.hasFocus){
13927             return;
13928         }
13929          if (!opts.add) {
13930             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13931          }
13932         this.restrictHeight();
13933         this.selectedIndex = -1;
13934     },
13935
13936     // private
13937     onLoad : function(){
13938         
13939         this.hasQuery = false;
13940         
13941         if(!this.hasFocus){
13942             return;
13943         }
13944         
13945         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13946             this.loading.hide();
13947         }
13948         
13949         if(this.store.getCount() > 0){
13950             
13951             this.expand();
13952             this.restrictHeight();
13953             if(this.lastQuery == this.allQuery){
13954                 if(this.editable && !this.tickable){
13955                     this.inputEl().dom.select();
13956                 }
13957                 
13958                 if(
13959                     !this.selectByValue(this.value, true) &&
13960                     this.autoFocus && 
13961                     (
13962                         !this.store.lastOptions ||
13963                         typeof(this.store.lastOptions.add) == 'undefined' || 
13964                         this.store.lastOptions.add != true
13965                     )
13966                 ){
13967                     this.select(0, true);
13968                 }
13969             }else{
13970                 if(this.autoFocus){
13971                     this.selectNext();
13972                 }
13973                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13974                     this.taTask.delay(this.typeAheadDelay);
13975                 }
13976             }
13977         }else{
13978             this.onEmptyResults();
13979         }
13980         
13981         //this.el.focus();
13982     },
13983     // private
13984     onLoadException : function()
13985     {
13986         this.hasQuery = false;
13987         
13988         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13989             this.loading.hide();
13990         }
13991         
13992         if(this.tickable && this.editable){
13993             return;
13994         }
13995         
13996         this.collapse();
13997         // only causes errors at present
13998         //Roo.log(this.store.reader.jsonData);
13999         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14000             // fixme
14001             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14002         //}
14003         
14004         
14005     },
14006     // private
14007     onTypeAhead : function(){
14008         if(this.store.getCount() > 0){
14009             var r = this.store.getAt(0);
14010             var newValue = r.data[this.displayField];
14011             var len = newValue.length;
14012             var selStart = this.getRawValue().length;
14013             
14014             if(selStart != len){
14015                 this.setRawValue(newValue);
14016                 this.selectText(selStart, newValue.length);
14017             }
14018         }
14019     },
14020
14021     // private
14022     onSelect : function(record, index){
14023         
14024         if(this.fireEvent('beforeselect', this, record, index) !== false){
14025         
14026             this.setFromData(index > -1 ? record.data : false);
14027             
14028             this.collapse();
14029             this.fireEvent('select', this, record, index);
14030         }
14031     },
14032
14033     /**
14034      * Returns the currently selected field value or empty string if no value is set.
14035      * @return {String} value The selected value
14036      */
14037     getValue : function()
14038     {
14039         if(Roo.isIOS && this.useNativeIOS){
14040             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14041         }
14042         
14043         if(this.multiple){
14044             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14045         }
14046         
14047         if(this.valueField){
14048             return typeof this.value != 'undefined' ? this.value : '';
14049         }else{
14050             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14051         }
14052     },
14053     
14054     getRawValue : function()
14055     {
14056         if(Roo.isIOS && this.useNativeIOS){
14057             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14058         }
14059         
14060         var v = this.inputEl().getValue();
14061         
14062         return v;
14063     },
14064
14065     /**
14066      * Clears any text/value currently set in the field
14067      */
14068     clearValue : function(){
14069         
14070         if(this.hiddenField){
14071             this.hiddenField.dom.value = '';
14072         }
14073         this.value = '';
14074         this.setRawValue('');
14075         this.lastSelectionText = '';
14076         this.lastData = false;
14077         
14078         var close = this.closeTriggerEl();
14079         
14080         if(close){
14081             close.hide();
14082         }
14083         
14084         this.validate();
14085         
14086     },
14087
14088     /**
14089      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14090      * will be displayed in the field.  If the value does not match the data value of an existing item,
14091      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14092      * Otherwise the field will be blank (although the value will still be set).
14093      * @param {String} value The value to match
14094      */
14095     setValue : function(v)
14096     {
14097         if(Roo.isIOS && this.useNativeIOS){
14098             this.setIOSValue(v);
14099             return;
14100         }
14101         
14102         if(this.multiple){
14103             this.syncValue();
14104             return;
14105         }
14106         
14107         var text = v;
14108         if(this.valueField){
14109             var r = this.findRecord(this.valueField, v);
14110             if(r){
14111                 text = r.data[this.displayField];
14112             }else if(this.valueNotFoundText !== undefined){
14113                 text = this.valueNotFoundText;
14114             }
14115         }
14116         this.lastSelectionText = text;
14117         if(this.hiddenField){
14118             this.hiddenField.dom.value = v;
14119         }
14120         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14121         this.value = v;
14122         
14123         var close = this.closeTriggerEl();
14124         
14125         if(close){
14126             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14127         }
14128         
14129         this.validate();
14130     },
14131     /**
14132      * @property {Object} the last set data for the element
14133      */
14134     
14135     lastData : false,
14136     /**
14137      * Sets the value of the field based on a object which is related to the record format for the store.
14138      * @param {Object} value the value to set as. or false on reset?
14139      */
14140     setFromData : function(o){
14141         
14142         if(this.multiple){
14143             this.addItem(o);
14144             return;
14145         }
14146             
14147         var dv = ''; // display value
14148         var vv = ''; // value value..
14149         this.lastData = o;
14150         if (this.displayField) {
14151             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14152         } else {
14153             // this is an error condition!!!
14154             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14155         }
14156         
14157         if(this.valueField){
14158             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14159         }
14160         
14161         var close = this.closeTriggerEl();
14162         
14163         if(close){
14164             if(dv.length || vv * 1 > 0){
14165                 close.show() ;
14166                 this.blockFocus=true;
14167             } else {
14168                 close.hide();
14169             }             
14170         }
14171         
14172         if(this.hiddenField){
14173             this.hiddenField.dom.value = vv;
14174             
14175             this.lastSelectionText = dv;
14176             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14177             this.value = vv;
14178             return;
14179         }
14180         // no hidden field.. - we store the value in 'value', but still display
14181         // display field!!!!
14182         this.lastSelectionText = dv;
14183         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14184         this.value = vv;
14185         
14186         
14187         
14188     },
14189     // private
14190     reset : function(){
14191         // overridden so that last data is reset..
14192         
14193         if(this.multiple){
14194             this.clearItem();
14195             return;
14196         }
14197         
14198         this.setValue(this.originalValue);
14199         //this.clearInvalid();
14200         this.lastData = false;
14201         if (this.view) {
14202             this.view.clearSelections();
14203         }
14204         
14205         this.validate();
14206     },
14207     // private
14208     findRecord : function(prop, value){
14209         var record;
14210         if(this.store.getCount() > 0){
14211             this.store.each(function(r){
14212                 if(r.data[prop] == value){
14213                     record = r;
14214                     return false;
14215                 }
14216                 return true;
14217             });
14218         }
14219         return record;
14220     },
14221     
14222     getName: function()
14223     {
14224         // returns hidden if it's set..
14225         if (!this.rendered) {return ''};
14226         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14227         
14228     },
14229     // private
14230     onViewMove : function(e, t){
14231         this.inKeyMode = false;
14232     },
14233
14234     // private
14235     onViewOver : function(e, t){
14236         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14237             return;
14238         }
14239         var item = this.view.findItemFromChild(t);
14240         
14241         if(item){
14242             var index = this.view.indexOf(item);
14243             this.select(index, false);
14244         }
14245     },
14246
14247     // private
14248     onViewClick : function(view, doFocus, el, e)
14249     {
14250         var index = this.view.getSelectedIndexes()[0];
14251         
14252         var r = this.store.getAt(index);
14253         
14254         if(this.tickable){
14255             
14256             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14257                 return;
14258             }
14259             
14260             var rm = false;
14261             var _this = this;
14262             
14263             Roo.each(this.tickItems, function(v,k){
14264                 
14265                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14266                     Roo.log(v);
14267                     _this.tickItems.splice(k, 1);
14268                     
14269                     if(typeof(e) == 'undefined' && view == false){
14270                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14271                     }
14272                     
14273                     rm = true;
14274                     return;
14275                 }
14276             });
14277             
14278             if(rm){
14279                 return;
14280             }
14281             
14282             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14283                 this.tickItems.push(r.data);
14284             }
14285             
14286             if(typeof(e) == 'undefined' && view == false){
14287                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14288             }
14289                     
14290             return;
14291         }
14292         
14293         if(r){
14294             this.onSelect(r, index);
14295         }
14296         if(doFocus !== false && !this.blockFocus){
14297             this.inputEl().focus();
14298         }
14299     },
14300
14301     // private
14302     restrictHeight : function(){
14303         //this.innerList.dom.style.height = '';
14304         //var inner = this.innerList.dom;
14305         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14306         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14307         //this.list.beginUpdate();
14308         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14309         this.list.alignTo(this.inputEl(), this.listAlign);
14310         this.list.alignTo(this.inputEl(), this.listAlign);
14311         //this.list.endUpdate();
14312     },
14313
14314     // private
14315     onEmptyResults : function(){
14316         
14317         if(this.tickable && this.editable){
14318             this.hasFocus = false;
14319             this.restrictHeight();
14320             return;
14321         }
14322         
14323         this.collapse();
14324     },
14325
14326     /**
14327      * Returns true if the dropdown list is expanded, else false.
14328      */
14329     isExpanded : function(){
14330         return this.list.isVisible();
14331     },
14332
14333     /**
14334      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14335      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14336      * @param {String} value The data value of the item to select
14337      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14338      * selected item if it is not currently in view (defaults to true)
14339      * @return {Boolean} True if the value matched an item in the list, else false
14340      */
14341     selectByValue : function(v, scrollIntoView){
14342         if(v !== undefined && v !== null){
14343             var r = this.findRecord(this.valueField || this.displayField, v);
14344             if(r){
14345                 this.select(this.store.indexOf(r), scrollIntoView);
14346                 return true;
14347             }
14348         }
14349         return false;
14350     },
14351
14352     /**
14353      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14354      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14355      * @param {Number} index The zero-based index of the list item to select
14356      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14357      * selected item if it is not currently in view (defaults to true)
14358      */
14359     select : function(index, scrollIntoView){
14360         this.selectedIndex = index;
14361         this.view.select(index);
14362         if(scrollIntoView !== false){
14363             var el = this.view.getNode(index);
14364             /*
14365              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14366              */
14367             if(el){
14368                 this.list.scrollChildIntoView(el, false);
14369             }
14370         }
14371     },
14372
14373     // private
14374     selectNext : function(){
14375         var ct = this.store.getCount();
14376         if(ct > 0){
14377             if(this.selectedIndex == -1){
14378                 this.select(0);
14379             }else if(this.selectedIndex < ct-1){
14380                 this.select(this.selectedIndex+1);
14381             }
14382         }
14383     },
14384
14385     // private
14386     selectPrev : function(){
14387         var ct = this.store.getCount();
14388         if(ct > 0){
14389             if(this.selectedIndex == -1){
14390                 this.select(0);
14391             }else if(this.selectedIndex != 0){
14392                 this.select(this.selectedIndex-1);
14393             }
14394         }
14395     },
14396
14397     // private
14398     onKeyUp : function(e){
14399         if(this.editable !== false && !e.isSpecialKey()){
14400             this.lastKey = e.getKey();
14401             this.dqTask.delay(this.queryDelay);
14402         }
14403     },
14404
14405     // private
14406     validateBlur : function(){
14407         return !this.list || !this.list.isVisible();   
14408     },
14409
14410     // private
14411     initQuery : function(){
14412         
14413         var v = this.getRawValue();
14414         
14415         if(this.tickable && this.editable){
14416             v = this.tickableInputEl().getValue();
14417         }
14418         
14419         this.doQuery(v);
14420     },
14421
14422     // private
14423     doForce : function(){
14424         if(this.inputEl().dom.value.length > 0){
14425             this.inputEl().dom.value =
14426                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14427              
14428         }
14429     },
14430
14431     /**
14432      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14433      * query allowing the query action to be canceled if needed.
14434      * @param {String} query The SQL query to execute
14435      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14436      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14437      * saved in the current store (defaults to false)
14438      */
14439     doQuery : function(q, forceAll){
14440         
14441         if(q === undefined || q === null){
14442             q = '';
14443         }
14444         var qe = {
14445             query: q,
14446             forceAll: forceAll,
14447             combo: this,
14448             cancel:false
14449         };
14450         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14451             return false;
14452         }
14453         q = qe.query;
14454         
14455         forceAll = qe.forceAll;
14456         if(forceAll === true || (q.length >= this.minChars)){
14457             
14458             this.hasQuery = true;
14459             
14460             if(this.lastQuery != q || this.alwaysQuery){
14461                 this.lastQuery = q;
14462                 if(this.mode == 'local'){
14463                     this.selectedIndex = -1;
14464                     if(forceAll){
14465                         this.store.clearFilter();
14466                     }else{
14467                         
14468                         if(this.specialFilter){
14469                             this.fireEvent('specialfilter', this);
14470                             this.onLoad();
14471                             return;
14472                         }
14473                         
14474                         this.store.filter(this.displayField, q);
14475                     }
14476                     
14477                     this.store.fireEvent("datachanged", this.store);
14478                     
14479                     this.onLoad();
14480                     
14481                     
14482                 }else{
14483                     
14484                     this.store.baseParams[this.queryParam] = q;
14485                     
14486                     var options = {params : this.getParams(q)};
14487                     
14488                     if(this.loadNext){
14489                         options.add = true;
14490                         options.params.start = this.page * this.pageSize;
14491                     }
14492                     
14493                     this.store.load(options);
14494                     
14495                     /*
14496                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14497                      *  we should expand the list on onLoad
14498                      *  so command out it
14499                      */
14500 //                    this.expand();
14501                 }
14502             }else{
14503                 this.selectedIndex = -1;
14504                 this.onLoad();   
14505             }
14506         }
14507         
14508         this.loadNext = false;
14509     },
14510     
14511     // private
14512     getParams : function(q){
14513         var p = {};
14514         //p[this.queryParam] = q;
14515         
14516         if(this.pageSize){
14517             p.start = 0;
14518             p.limit = this.pageSize;
14519         }
14520         return p;
14521     },
14522
14523     /**
14524      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14525      */
14526     collapse : function(){
14527         if(!this.isExpanded()){
14528             return;
14529         }
14530         
14531         this.list.hide();
14532         
14533         this.hasFocus = false;
14534         
14535         if(this.tickable){
14536             this.okBtn.hide();
14537             this.cancelBtn.hide();
14538             this.trigger.show();
14539             
14540             if(this.editable){
14541                 this.tickableInputEl().dom.value = '';
14542                 this.tickableInputEl().blur();
14543             }
14544             
14545         }
14546         
14547         Roo.get(document).un('mousedown', this.collapseIf, this);
14548         Roo.get(document).un('mousewheel', this.collapseIf, this);
14549         if (!this.editable) {
14550             Roo.get(document).un('keydown', this.listKeyPress, this);
14551         }
14552         this.fireEvent('collapse', this);
14553         
14554         this.validate();
14555     },
14556
14557     // private
14558     collapseIf : function(e){
14559         var in_combo  = e.within(this.el);
14560         var in_list =  e.within(this.list);
14561         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14562         
14563         if (in_combo || in_list || is_list) {
14564             //e.stopPropagation();
14565             return;
14566         }
14567         
14568         if(this.tickable){
14569             this.onTickableFooterButtonClick(e, false, false);
14570         }
14571
14572         this.collapse();
14573         
14574     },
14575
14576     /**
14577      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14578      */
14579     expand : function(){
14580        
14581         if(this.isExpanded() || !this.hasFocus){
14582             return;
14583         }
14584         
14585         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14586         this.list.setWidth(lw);
14587         
14588         Roo.log('expand');
14589         
14590         this.list.show();
14591         
14592         this.restrictHeight();
14593         
14594         if(this.tickable){
14595             
14596             this.tickItems = Roo.apply([], this.item);
14597             
14598             this.okBtn.show();
14599             this.cancelBtn.show();
14600             this.trigger.hide();
14601             
14602             if(this.editable){
14603                 this.tickableInputEl().focus();
14604             }
14605             
14606         }
14607         
14608         Roo.get(document).on('mousedown', this.collapseIf, this);
14609         Roo.get(document).on('mousewheel', this.collapseIf, this);
14610         if (!this.editable) {
14611             Roo.get(document).on('keydown', this.listKeyPress, this);
14612         }
14613         
14614         this.fireEvent('expand', this);
14615     },
14616
14617     // private
14618     // Implements the default empty TriggerField.onTriggerClick function
14619     onTriggerClick : function(e)
14620     {
14621         Roo.log('trigger click');
14622         
14623         if(this.disabled || !this.triggerList){
14624             return;
14625         }
14626         
14627         this.page = 0;
14628         this.loadNext = false;
14629         
14630         if(this.isExpanded()){
14631             this.collapse();
14632             if (!this.blockFocus) {
14633                 this.inputEl().focus();
14634             }
14635             
14636         }else {
14637             this.hasFocus = true;
14638             if(this.triggerAction == 'all') {
14639                 this.doQuery(this.allQuery, true);
14640             } else {
14641                 this.doQuery(this.getRawValue());
14642             }
14643             if (!this.blockFocus) {
14644                 this.inputEl().focus();
14645             }
14646         }
14647     },
14648     
14649     onTickableTriggerClick : function(e)
14650     {
14651         if(this.disabled){
14652             return;
14653         }
14654         
14655         this.page = 0;
14656         this.loadNext = false;
14657         this.hasFocus = true;
14658         
14659         if(this.triggerAction == 'all') {
14660             this.doQuery(this.allQuery, true);
14661         } else {
14662             this.doQuery(this.getRawValue());
14663         }
14664     },
14665     
14666     onSearchFieldClick : function(e)
14667     {
14668         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14669             this.onTickableFooterButtonClick(e, false, false);
14670             return;
14671         }
14672         
14673         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14674             return;
14675         }
14676         
14677         this.page = 0;
14678         this.loadNext = false;
14679         this.hasFocus = true;
14680         
14681         if(this.triggerAction == 'all') {
14682             this.doQuery(this.allQuery, true);
14683         } else {
14684             this.doQuery(this.getRawValue());
14685         }
14686     },
14687     
14688     listKeyPress : function(e)
14689     {
14690         //Roo.log('listkeypress');
14691         // scroll to first matching element based on key pres..
14692         if (e.isSpecialKey()) {
14693             return false;
14694         }
14695         var k = String.fromCharCode(e.getKey()).toUpperCase();
14696         //Roo.log(k);
14697         var match  = false;
14698         var csel = this.view.getSelectedNodes();
14699         var cselitem = false;
14700         if (csel.length) {
14701             var ix = this.view.indexOf(csel[0]);
14702             cselitem  = this.store.getAt(ix);
14703             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14704                 cselitem = false;
14705             }
14706             
14707         }
14708         
14709         this.store.each(function(v) { 
14710             if (cselitem) {
14711                 // start at existing selection.
14712                 if (cselitem.id == v.id) {
14713                     cselitem = false;
14714                 }
14715                 return true;
14716             }
14717                 
14718             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14719                 match = this.store.indexOf(v);
14720                 return false;
14721             }
14722             return true;
14723         }, this);
14724         
14725         if (match === false) {
14726             return true; // no more action?
14727         }
14728         // scroll to?
14729         this.view.select(match);
14730         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14731         sn.scrollIntoView(sn.dom.parentNode, false);
14732     },
14733     
14734     onViewScroll : function(e, t){
14735         
14736         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){
14737             return;
14738         }
14739         
14740         this.hasQuery = true;
14741         
14742         this.loading = this.list.select('.loading', true).first();
14743         
14744         if(this.loading === null){
14745             this.list.createChild({
14746                 tag: 'div',
14747                 cls: 'loading roo-select2-more-results roo-select2-active',
14748                 html: 'Loading more results...'
14749             });
14750             
14751             this.loading = this.list.select('.loading', true).first();
14752             
14753             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14754             
14755             this.loading.hide();
14756         }
14757         
14758         this.loading.show();
14759         
14760         var _combo = this;
14761         
14762         this.page++;
14763         this.loadNext = true;
14764         
14765         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14766         
14767         return;
14768     },
14769     
14770     addItem : function(o)
14771     {   
14772         var dv = ''; // display value
14773         
14774         if (this.displayField) {
14775             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14776         } else {
14777             // this is an error condition!!!
14778             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14779         }
14780         
14781         if(!dv.length){
14782             return;
14783         }
14784         
14785         var choice = this.choices.createChild({
14786             tag: 'li',
14787             cls: 'roo-select2-search-choice',
14788             cn: [
14789                 {
14790                     tag: 'div',
14791                     html: dv
14792                 },
14793                 {
14794                     tag: 'a',
14795                     href: '#',
14796                     cls: 'roo-select2-search-choice-close fa fa-times',
14797                     tabindex: '-1'
14798                 }
14799             ]
14800             
14801         }, this.searchField);
14802         
14803         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14804         
14805         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14806         
14807         this.item.push(o);
14808         
14809         this.lastData = o;
14810         
14811         this.syncValue();
14812         
14813         this.inputEl().dom.value = '';
14814         
14815         this.validate();
14816     },
14817     
14818     onRemoveItem : function(e, _self, o)
14819     {
14820         e.preventDefault();
14821         
14822         this.lastItem = Roo.apply([], this.item);
14823         
14824         var index = this.item.indexOf(o.data) * 1;
14825         
14826         if( index < 0){
14827             Roo.log('not this item?!');
14828             return;
14829         }
14830         
14831         this.item.splice(index, 1);
14832         o.item.remove();
14833         
14834         this.syncValue();
14835         
14836         this.fireEvent('remove', this, e);
14837         
14838         this.validate();
14839         
14840     },
14841     
14842     syncValue : function()
14843     {
14844         if(!this.item.length){
14845             this.clearValue();
14846             return;
14847         }
14848             
14849         var value = [];
14850         var _this = this;
14851         Roo.each(this.item, function(i){
14852             if(_this.valueField){
14853                 value.push(i[_this.valueField]);
14854                 return;
14855             }
14856
14857             value.push(i);
14858         });
14859
14860         this.value = value.join(',');
14861
14862         if(this.hiddenField){
14863             this.hiddenField.dom.value = this.value;
14864         }
14865         
14866         this.store.fireEvent("datachanged", this.store);
14867         
14868         this.validate();
14869     },
14870     
14871     clearItem : function()
14872     {
14873         if(!this.multiple){
14874             return;
14875         }
14876         
14877         this.item = [];
14878         
14879         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14880            c.remove();
14881         });
14882         
14883         this.syncValue();
14884         
14885         this.validate();
14886         
14887         if(this.tickable && !Roo.isTouch){
14888             this.view.refresh();
14889         }
14890     },
14891     
14892     inputEl: function ()
14893     {
14894         if(Roo.isIOS && this.useNativeIOS){
14895             return this.el.select('select.roo-ios-select', true).first();
14896         }
14897         
14898         if(Roo.isTouch && this.mobileTouchView){
14899             return this.el.select('input.form-control',true).first();
14900         }
14901         
14902         if(this.tickable){
14903             return this.searchField;
14904         }
14905         
14906         return this.el.select('input.form-control',true).first();
14907     },
14908     
14909     onTickableFooterButtonClick : function(e, btn, el)
14910     {
14911         e.preventDefault();
14912         
14913         this.lastItem = Roo.apply([], this.item);
14914         
14915         if(btn && btn.name == 'cancel'){
14916             this.tickItems = Roo.apply([], this.item);
14917             this.collapse();
14918             return;
14919         }
14920         
14921         this.clearItem();
14922         
14923         var _this = this;
14924         
14925         Roo.each(this.tickItems, function(o){
14926             _this.addItem(o);
14927         });
14928         
14929         this.collapse();
14930         
14931     },
14932     
14933     validate : function()
14934     {
14935         if(this.getVisibilityEl().hasClass('hidden')){
14936             return true;
14937         }
14938         
14939         var v = this.getRawValue();
14940         
14941         if(this.multiple){
14942             v = this.getValue();
14943         }
14944         
14945         if(this.disabled || this.allowBlank || v.length){
14946             this.markValid();
14947             return true;
14948         }
14949         
14950         this.markInvalid();
14951         return false;
14952     },
14953     
14954     tickableInputEl : function()
14955     {
14956         if(!this.tickable || !this.editable){
14957             return this.inputEl();
14958         }
14959         
14960         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14961     },
14962     
14963     
14964     getAutoCreateTouchView : function()
14965     {
14966         var id = Roo.id();
14967         
14968         var cfg = {
14969             cls: 'form-group' //input-group
14970         };
14971         
14972         var input =  {
14973             tag: 'input',
14974             id : id,
14975             type : this.inputType,
14976             cls : 'form-control x-combo-noedit',
14977             autocomplete: 'new-password',
14978             placeholder : this.placeholder || '',
14979             readonly : true
14980         };
14981         
14982         if (this.name) {
14983             input.name = this.name;
14984         }
14985         
14986         if (this.size) {
14987             input.cls += ' input-' + this.size;
14988         }
14989         
14990         if (this.disabled) {
14991             input.disabled = true;
14992         }
14993         
14994         var inputblock = {
14995             cls : '',
14996             cn : [
14997                 input
14998             ]
14999         };
15000         
15001         if(this.before){
15002             inputblock.cls += ' input-group';
15003             
15004             inputblock.cn.unshift({
15005                 tag :'span',
15006                 cls : 'input-group-addon',
15007                 html : this.before
15008             });
15009         }
15010         
15011         if(this.removable && !this.multiple){
15012             inputblock.cls += ' roo-removable';
15013             
15014             inputblock.cn.push({
15015                 tag: 'button',
15016                 html : 'x',
15017                 cls : 'roo-combo-removable-btn close'
15018             });
15019         }
15020
15021         if(this.hasFeedback && !this.allowBlank){
15022             
15023             inputblock.cls += ' has-feedback';
15024             
15025             inputblock.cn.push({
15026                 tag: 'span',
15027                 cls: 'glyphicon form-control-feedback'
15028             });
15029             
15030         }
15031         
15032         if (this.after) {
15033             
15034             inputblock.cls += (this.before) ? '' : ' input-group';
15035             
15036             inputblock.cn.push({
15037                 tag :'span',
15038                 cls : 'input-group-addon',
15039                 html : this.after
15040             });
15041         }
15042
15043         var box = {
15044             tag: 'div',
15045             cn: [
15046                 {
15047                     tag: 'input',
15048                     type : 'hidden',
15049                     cls: 'form-hidden-field'
15050                 },
15051                 inputblock
15052             ]
15053             
15054         };
15055         
15056         if(this.multiple){
15057             box = {
15058                 tag: 'div',
15059                 cn: [
15060                     {
15061                         tag: 'input',
15062                         type : 'hidden',
15063                         cls: 'form-hidden-field'
15064                     },
15065                     {
15066                         tag: 'ul',
15067                         cls: 'roo-select2-choices',
15068                         cn:[
15069                             {
15070                                 tag: 'li',
15071                                 cls: 'roo-select2-search-field',
15072                                 cn: [
15073
15074                                     inputblock
15075                                 ]
15076                             }
15077                         ]
15078                     }
15079                 ]
15080             }
15081         };
15082         
15083         var combobox = {
15084             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15085             cn: [
15086                 box
15087             ]
15088         };
15089         
15090         if(!this.multiple && this.showToggleBtn){
15091             
15092             var caret = {
15093                         tag: 'span',
15094                         cls: 'caret'
15095             };
15096             
15097             if (this.caret != false) {
15098                 caret = {
15099                      tag: 'i',
15100                      cls: 'fa fa-' + this.caret
15101                 };
15102                 
15103             }
15104             
15105             combobox.cn.push({
15106                 tag :'span',
15107                 cls : 'input-group-addon btn dropdown-toggle',
15108                 cn : [
15109                     caret,
15110                     {
15111                         tag: 'span',
15112                         cls: 'combobox-clear',
15113                         cn  : [
15114                             {
15115                                 tag : 'i',
15116                                 cls: 'icon-remove'
15117                             }
15118                         ]
15119                     }
15120                 ]
15121
15122             })
15123         }
15124         
15125         if(this.multiple){
15126             combobox.cls += ' roo-select2-container-multi';
15127         }
15128         
15129         var align = this.labelAlign || this.parentLabelAlign();
15130         
15131         if (align ==='left' && this.fieldLabel.length) {
15132
15133             cfg.cn = [
15134                 {
15135                    tag : 'i',
15136                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15137                    tooltip : 'This field is required'
15138                 },
15139                 {
15140                     tag: 'label',
15141                     cls : 'control-label',
15142                     html : this.fieldLabel
15143
15144                 },
15145                 {
15146                     cls : '', 
15147                     cn: [
15148                         combobox
15149                     ]
15150                 }
15151             ];
15152             
15153             var labelCfg = cfg.cn[1];
15154             var contentCfg = cfg.cn[2];
15155             
15156
15157             if(this.indicatorpos == 'right'){
15158                 cfg.cn = [
15159                     {
15160                         tag: 'label',
15161                         'for' :  id,
15162                         cls : 'control-label',
15163                         cn : [
15164                             {
15165                                 tag : 'span',
15166                                 html : this.fieldLabel
15167                             },
15168                             {
15169                                 tag : 'i',
15170                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15171                                 tooltip : 'This field is required'
15172                             }
15173                         ]
15174                     },
15175                     {
15176                         cls : "",
15177                         cn: [
15178                             combobox
15179                         ]
15180                     }
15181
15182                 ];
15183                 
15184                 labelCfg = cfg.cn[0];
15185                 contentCfg = cfg.cn[1];
15186             }
15187             
15188            
15189             
15190             if(this.labelWidth > 12){
15191                 labelCfg.style = "width: " + this.labelWidth + 'px';
15192             }
15193             
15194             if(this.labelWidth < 13 && this.labelmd == 0){
15195                 this.labelmd = this.labelWidth;
15196             }
15197             
15198             if(this.labellg > 0){
15199                 labelCfg.cls += ' col-lg-' + this.labellg;
15200                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15201             }
15202             
15203             if(this.labelmd > 0){
15204                 labelCfg.cls += ' col-md-' + this.labelmd;
15205                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15206             }
15207             
15208             if(this.labelsm > 0){
15209                 labelCfg.cls += ' col-sm-' + this.labelsm;
15210                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15211             }
15212             
15213             if(this.labelxs > 0){
15214                 labelCfg.cls += ' col-xs-' + this.labelxs;
15215                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15216             }
15217                 
15218                 
15219         } else if ( this.fieldLabel.length) {
15220             cfg.cn = [
15221                 {
15222                    tag : 'i',
15223                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15224                    tooltip : 'This field is required'
15225                 },
15226                 {
15227                     tag: 'label',
15228                     cls : 'control-label',
15229                     html : this.fieldLabel
15230
15231                 },
15232                 {
15233                     cls : '', 
15234                     cn: [
15235                         combobox
15236                     ]
15237                 }
15238             ];
15239             
15240             if(this.indicatorpos == 'right'){
15241                 cfg.cn = [
15242                     {
15243                         tag: 'label',
15244                         cls : 'control-label',
15245                         html : this.fieldLabel,
15246                         cn : [
15247                             {
15248                                tag : 'i',
15249                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15250                                tooltip : 'This field is required'
15251                             }
15252                         ]
15253                     },
15254                     {
15255                         cls : '', 
15256                         cn: [
15257                             combobox
15258                         ]
15259                     }
15260                 ];
15261             }
15262         } else {
15263             cfg.cn = combobox;    
15264         }
15265         
15266         
15267         var settings = this;
15268         
15269         ['xs','sm','md','lg'].map(function(size){
15270             if (settings[size]) {
15271                 cfg.cls += ' col-' + size + '-' + settings[size];
15272             }
15273         });
15274         
15275         return cfg;
15276     },
15277     
15278     initTouchView : function()
15279     {
15280         this.renderTouchView();
15281         
15282         this.touchViewEl.on('scroll', function(){
15283             this.el.dom.scrollTop = 0;
15284         }, this);
15285         
15286         this.originalValue = this.getValue();
15287         
15288         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15289         
15290         this.inputEl().on("click", this.showTouchView, this);
15291         if (this.triggerEl) {
15292             this.triggerEl.on("click", this.showTouchView, this);
15293         }
15294         
15295         
15296         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15297         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15298         
15299         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15300         
15301         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15302         this.store.on('load', this.onTouchViewLoad, this);
15303         this.store.on('loadexception', this.onTouchViewLoadException, this);
15304         
15305         if(this.hiddenName){
15306             
15307             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15308             
15309             this.hiddenField.dom.value =
15310                 this.hiddenValue !== undefined ? this.hiddenValue :
15311                 this.value !== undefined ? this.value : '';
15312         
15313             this.el.dom.removeAttribute('name');
15314             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15315         }
15316         
15317         if(this.multiple){
15318             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15319             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15320         }
15321         
15322         if(this.removable && !this.multiple){
15323             var close = this.closeTriggerEl();
15324             if(close){
15325                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15326                 close.on('click', this.removeBtnClick, this, close);
15327             }
15328         }
15329         /*
15330          * fix the bug in Safari iOS8
15331          */
15332         this.inputEl().on("focus", function(e){
15333             document.activeElement.blur();
15334         }, this);
15335         
15336         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15337         
15338         return;
15339         
15340         
15341     },
15342     
15343     renderTouchView : function()
15344     {
15345         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15346         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15347         
15348         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15349         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15350         
15351         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15352         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15353         this.touchViewBodyEl.setStyle('overflow', 'auto');
15354         
15355         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15356         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15357         
15358         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15359         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15360         
15361     },
15362     
15363     showTouchView : function()
15364     {
15365         if(this.disabled){
15366             return;
15367         }
15368         
15369         this.touchViewHeaderEl.hide();
15370
15371         if(this.modalTitle.length){
15372             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15373             this.touchViewHeaderEl.show();
15374         }
15375
15376         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15377         this.touchViewEl.show();
15378
15379         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15380         
15381         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15382         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15383
15384         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15385
15386         if(this.modalTitle.length){
15387             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15388         }
15389         
15390         this.touchViewBodyEl.setHeight(bodyHeight);
15391
15392         if(this.animate){
15393             var _this = this;
15394             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15395         }else{
15396             this.touchViewEl.addClass('in');
15397         }
15398         
15399         if(this._touchViewMask){
15400             Roo.get(document.body).addClass("x-body-masked");
15401             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15402             this._touchViewMask.setStyle('z-index', 10000);
15403             this._touchViewMask.addClass('show');
15404         }
15405         
15406         this.doTouchViewQuery();
15407         
15408     },
15409     
15410     hideTouchView : function()
15411     {
15412         this.touchViewEl.removeClass('in');
15413
15414         if(this.animate){
15415             var _this = this;
15416             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15417         }else{
15418             this.touchViewEl.setStyle('display', 'none');
15419         }
15420         
15421         if(this._touchViewMask){
15422             this._touchViewMask.removeClass('show');
15423             Roo.get(document.body).removeClass("x-body-masked");
15424         }
15425     },
15426     
15427     setTouchViewValue : function()
15428     {
15429         if(this.multiple){
15430             this.clearItem();
15431         
15432             var _this = this;
15433
15434             Roo.each(this.tickItems, function(o){
15435                 this.addItem(o);
15436             }, this);
15437         }
15438         
15439         this.hideTouchView();
15440     },
15441     
15442     doTouchViewQuery : function()
15443     {
15444         var qe = {
15445             query: '',
15446             forceAll: true,
15447             combo: this,
15448             cancel:false
15449         };
15450         
15451         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15452             return false;
15453         }
15454         
15455         if(!this.alwaysQuery || this.mode == 'local'){
15456             this.onTouchViewLoad();
15457             return;
15458         }
15459         
15460         this.store.load();
15461     },
15462     
15463     onTouchViewBeforeLoad : function(combo,opts)
15464     {
15465         return;
15466     },
15467
15468     // private
15469     onTouchViewLoad : function()
15470     {
15471         if(this.store.getCount() < 1){
15472             this.onTouchViewEmptyResults();
15473             return;
15474         }
15475         
15476         this.clearTouchView();
15477         
15478         var rawValue = this.getRawValue();
15479         
15480         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15481         
15482         this.tickItems = [];
15483         
15484         this.store.data.each(function(d, rowIndex){
15485             var row = this.touchViewListGroup.createChild(template);
15486             
15487             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15488                 row.addClass(d.data.cls);
15489             }
15490             
15491             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15492                 var cfg = {
15493                     data : d.data,
15494                     html : d.data[this.displayField]
15495                 };
15496                 
15497                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15498                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15499                 }
15500             }
15501             row.removeClass('selected');
15502             if(!this.multiple && this.valueField &&
15503                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15504             {
15505                 // radio buttons..
15506                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15507                 row.addClass('selected');
15508             }
15509             
15510             if(this.multiple && this.valueField &&
15511                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15512             {
15513                 
15514                 // checkboxes...
15515                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15516                 this.tickItems.push(d.data);
15517             }
15518             
15519             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15520             
15521         }, this);
15522         
15523         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15524         
15525         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15526
15527         if(this.modalTitle.length){
15528             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15529         }
15530
15531         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15532         
15533         if(this.mobile_restrict_height && listHeight < bodyHeight){
15534             this.touchViewBodyEl.setHeight(listHeight);
15535         }
15536         
15537         var _this = this;
15538         
15539         if(firstChecked && listHeight > bodyHeight){
15540             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15541         }
15542         
15543     },
15544     
15545     onTouchViewLoadException : function()
15546     {
15547         this.hideTouchView();
15548     },
15549     
15550     onTouchViewEmptyResults : function()
15551     {
15552         this.clearTouchView();
15553         
15554         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15555         
15556         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15557         
15558     },
15559     
15560     clearTouchView : function()
15561     {
15562         this.touchViewListGroup.dom.innerHTML = '';
15563     },
15564     
15565     onTouchViewClick : function(e, el, o)
15566     {
15567         e.preventDefault();
15568         
15569         var row = o.row;
15570         var rowIndex = o.rowIndex;
15571         
15572         var r = this.store.getAt(rowIndex);
15573         
15574         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15575             
15576             if(!this.multiple){
15577                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15578                     c.dom.removeAttribute('checked');
15579                 }, this);
15580
15581                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15582
15583                 this.setFromData(r.data);
15584
15585                 var close = this.closeTriggerEl();
15586
15587                 if(close){
15588                     close.show();
15589                 }
15590
15591                 this.hideTouchView();
15592
15593                 this.fireEvent('select', this, r, rowIndex);
15594
15595                 return;
15596             }
15597
15598             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15599                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15600                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15601                 return;
15602             }
15603
15604             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15605             this.addItem(r.data);
15606             this.tickItems.push(r.data);
15607         }
15608     },
15609     
15610     getAutoCreateNativeIOS : function()
15611     {
15612         var cfg = {
15613             cls: 'form-group' //input-group,
15614         };
15615         
15616         var combobox =  {
15617             tag: 'select',
15618             cls : 'roo-ios-select'
15619         };
15620         
15621         if (this.name) {
15622             combobox.name = this.name;
15623         }
15624         
15625         if (this.disabled) {
15626             combobox.disabled = true;
15627         }
15628         
15629         var settings = this;
15630         
15631         ['xs','sm','md','lg'].map(function(size){
15632             if (settings[size]) {
15633                 cfg.cls += ' col-' + size + '-' + settings[size];
15634             }
15635         });
15636         
15637         cfg.cn = combobox;
15638         
15639         return cfg;
15640         
15641     },
15642     
15643     initIOSView : function()
15644     {
15645         this.store.on('load', this.onIOSViewLoad, this);
15646         
15647         return;
15648     },
15649     
15650     onIOSViewLoad : function()
15651     {
15652         if(this.store.getCount() < 1){
15653             return;
15654         }
15655         
15656         this.clearIOSView();
15657         
15658         if(this.allowBlank) {
15659             
15660             var default_text = '-- SELECT --';
15661             
15662             if(this.placeholder.length){
15663                 default_text = this.placeholder;
15664             }
15665             
15666             if(this.emptyTitle.length){
15667                 default_text += ' - ' + this.emptyTitle + ' -';
15668             }
15669             
15670             var opt = this.inputEl().createChild({
15671                 tag: 'option',
15672                 value : 0,
15673                 html : default_text
15674             });
15675             
15676             var o = {};
15677             o[this.valueField] = 0;
15678             o[this.displayField] = default_text;
15679             
15680             this.ios_options.push({
15681                 data : o,
15682                 el : opt
15683             });
15684             
15685         }
15686         
15687         this.store.data.each(function(d, rowIndex){
15688             
15689             var html = '';
15690             
15691             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15692                 html = d.data[this.displayField];
15693             }
15694             
15695             var value = '';
15696             
15697             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15698                 value = d.data[this.valueField];
15699             }
15700             
15701             var option = {
15702                 tag: 'option',
15703                 value : value,
15704                 html : html
15705             };
15706             
15707             if(this.value == d.data[this.valueField]){
15708                 option['selected'] = true;
15709             }
15710             
15711             var opt = this.inputEl().createChild(option);
15712             
15713             this.ios_options.push({
15714                 data : d.data,
15715                 el : opt
15716             });
15717             
15718         }, this);
15719         
15720         this.inputEl().on('change', function(){
15721            this.fireEvent('select', this);
15722         }, this);
15723         
15724     },
15725     
15726     clearIOSView: function()
15727     {
15728         this.inputEl().dom.innerHTML = '';
15729         
15730         this.ios_options = [];
15731     },
15732     
15733     setIOSValue: function(v)
15734     {
15735         this.value = v;
15736         
15737         if(!this.ios_options){
15738             return;
15739         }
15740         
15741         Roo.each(this.ios_options, function(opts){
15742            
15743            opts.el.dom.removeAttribute('selected');
15744            
15745            if(opts.data[this.valueField] != v){
15746                return;
15747            }
15748            
15749            opts.el.dom.setAttribute('selected', true);
15750            
15751         }, this);
15752     }
15753
15754     /** 
15755     * @cfg {Boolean} grow 
15756     * @hide 
15757     */
15758     /** 
15759     * @cfg {Number} growMin 
15760     * @hide 
15761     */
15762     /** 
15763     * @cfg {Number} growMax 
15764     * @hide 
15765     */
15766     /**
15767      * @hide
15768      * @method autoSize
15769      */
15770 });
15771
15772 Roo.apply(Roo.bootstrap.ComboBox,  {
15773     
15774     header : {
15775         tag: 'div',
15776         cls: 'modal-header',
15777         cn: [
15778             {
15779                 tag: 'h4',
15780                 cls: 'modal-title'
15781             }
15782         ]
15783     },
15784     
15785     body : {
15786         tag: 'div',
15787         cls: 'modal-body',
15788         cn: [
15789             {
15790                 tag: 'ul',
15791                 cls: 'list-group'
15792             }
15793         ]
15794     },
15795     
15796     listItemRadio : {
15797         tag: 'li',
15798         cls: 'list-group-item',
15799         cn: [
15800             {
15801                 tag: 'span',
15802                 cls: 'roo-combobox-list-group-item-value'
15803             },
15804             {
15805                 tag: 'div',
15806                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15807                 cn: [
15808                     {
15809                         tag: 'input',
15810                         type: 'radio'
15811                     },
15812                     {
15813                         tag: 'label'
15814                     }
15815                 ]
15816             }
15817         ]
15818     },
15819     
15820     listItemCheckbox : {
15821         tag: 'li',
15822         cls: 'list-group-item',
15823         cn: [
15824             {
15825                 tag: 'span',
15826                 cls: 'roo-combobox-list-group-item-value'
15827             },
15828             {
15829                 tag: 'div',
15830                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15831                 cn: [
15832                     {
15833                         tag: 'input',
15834                         type: 'checkbox'
15835                     },
15836                     {
15837                         tag: 'label'
15838                     }
15839                 ]
15840             }
15841         ]
15842     },
15843     
15844     emptyResult : {
15845         tag: 'div',
15846         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15847     },
15848     
15849     footer : {
15850         tag: 'div',
15851         cls: 'modal-footer',
15852         cn: [
15853             {
15854                 tag: 'div',
15855                 cls: 'row',
15856                 cn: [
15857                     {
15858                         tag: 'div',
15859                         cls: 'col-xs-6 text-left',
15860                         cn: {
15861                             tag: 'button',
15862                             cls: 'btn btn-danger roo-touch-view-cancel',
15863                             html: 'Cancel'
15864                         }
15865                     },
15866                     {
15867                         tag: 'div',
15868                         cls: 'col-xs-6 text-right',
15869                         cn: {
15870                             tag: 'button',
15871                             cls: 'btn btn-success roo-touch-view-ok',
15872                             html: 'OK'
15873                         }
15874                     }
15875                 ]
15876             }
15877         ]
15878         
15879     }
15880 });
15881
15882 Roo.apply(Roo.bootstrap.ComboBox,  {
15883     
15884     touchViewTemplate : {
15885         tag: 'div',
15886         cls: 'modal fade roo-combobox-touch-view',
15887         cn: [
15888             {
15889                 tag: 'div',
15890                 cls: 'modal-dialog',
15891                 style : 'position:fixed', // we have to fix position....
15892                 cn: [
15893                     {
15894                         tag: 'div',
15895                         cls: 'modal-content',
15896                         cn: [
15897                             Roo.bootstrap.ComboBox.header,
15898                             Roo.bootstrap.ComboBox.body,
15899                             Roo.bootstrap.ComboBox.footer
15900                         ]
15901                     }
15902                 ]
15903             }
15904         ]
15905     }
15906 });/*
15907  * Based on:
15908  * Ext JS Library 1.1.1
15909  * Copyright(c) 2006-2007, Ext JS, LLC.
15910  *
15911  * Originally Released Under LGPL - original licence link has changed is not relivant.
15912  *
15913  * Fork - LGPL
15914  * <script type="text/javascript">
15915  */
15916
15917 /**
15918  * @class Roo.View
15919  * @extends Roo.util.Observable
15920  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15921  * This class also supports single and multi selection modes. <br>
15922  * Create a data model bound view:
15923  <pre><code>
15924  var store = new Roo.data.Store(...);
15925
15926  var view = new Roo.View({
15927     el : "my-element",
15928     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15929  
15930     singleSelect: true,
15931     selectedClass: "ydataview-selected",
15932     store: store
15933  });
15934
15935  // listen for node click?
15936  view.on("click", function(vw, index, node, e){
15937  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15938  });
15939
15940  // load XML data
15941  dataModel.load("foobar.xml");
15942  </code></pre>
15943  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15944  * <br><br>
15945  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15946  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15947  * 
15948  * Note: old style constructor is still suported (container, template, config)
15949  * 
15950  * @constructor
15951  * Create a new View
15952  * @param {Object} config The config object
15953  * 
15954  */
15955 Roo.View = function(config, depreciated_tpl, depreciated_config){
15956     
15957     this.parent = false;
15958     
15959     if (typeof(depreciated_tpl) == 'undefined') {
15960         // new way.. - universal constructor.
15961         Roo.apply(this, config);
15962         this.el  = Roo.get(this.el);
15963     } else {
15964         // old format..
15965         this.el  = Roo.get(config);
15966         this.tpl = depreciated_tpl;
15967         Roo.apply(this, depreciated_config);
15968     }
15969     this.wrapEl  = this.el.wrap().wrap();
15970     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15971     
15972     
15973     if(typeof(this.tpl) == "string"){
15974         this.tpl = new Roo.Template(this.tpl);
15975     } else {
15976         // support xtype ctors..
15977         this.tpl = new Roo.factory(this.tpl, Roo);
15978     }
15979     
15980     
15981     this.tpl.compile();
15982     
15983     /** @private */
15984     this.addEvents({
15985         /**
15986          * @event beforeclick
15987          * Fires before a click is processed. Returns false to cancel the default action.
15988          * @param {Roo.View} this
15989          * @param {Number} index The index of the target node
15990          * @param {HTMLElement} node The target node
15991          * @param {Roo.EventObject} e The raw event object
15992          */
15993             "beforeclick" : true,
15994         /**
15995          * @event click
15996          * Fires when a template node is clicked.
15997          * @param {Roo.View} this
15998          * @param {Number} index The index of the target node
15999          * @param {HTMLElement} node The target node
16000          * @param {Roo.EventObject} e The raw event object
16001          */
16002             "click" : true,
16003         /**
16004          * @event dblclick
16005          * Fires when a template node is double clicked.
16006          * @param {Roo.View} this
16007          * @param {Number} index The index of the target node
16008          * @param {HTMLElement} node The target node
16009          * @param {Roo.EventObject} e The raw event object
16010          */
16011             "dblclick" : true,
16012         /**
16013          * @event contextmenu
16014          * Fires when a template node is right clicked.
16015          * @param {Roo.View} this
16016          * @param {Number} index The index of the target node
16017          * @param {HTMLElement} node The target node
16018          * @param {Roo.EventObject} e The raw event object
16019          */
16020             "contextmenu" : true,
16021         /**
16022          * @event selectionchange
16023          * Fires when the selected nodes change.
16024          * @param {Roo.View} this
16025          * @param {Array} selections Array of the selected nodes
16026          */
16027             "selectionchange" : true,
16028     
16029         /**
16030          * @event beforeselect
16031          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16032          * @param {Roo.View} this
16033          * @param {HTMLElement} node The node to be selected
16034          * @param {Array} selections Array of currently selected nodes
16035          */
16036             "beforeselect" : true,
16037         /**
16038          * @event preparedata
16039          * Fires on every row to render, to allow you to change the data.
16040          * @param {Roo.View} this
16041          * @param {Object} data to be rendered (change this)
16042          */
16043           "preparedata" : true
16044           
16045           
16046         });
16047
16048
16049
16050     this.el.on({
16051         "click": this.onClick,
16052         "dblclick": this.onDblClick,
16053         "contextmenu": this.onContextMenu,
16054         scope:this
16055     });
16056
16057     this.selections = [];
16058     this.nodes = [];
16059     this.cmp = new Roo.CompositeElementLite([]);
16060     if(this.store){
16061         this.store = Roo.factory(this.store, Roo.data);
16062         this.setStore(this.store, true);
16063     }
16064     
16065     if ( this.footer && this.footer.xtype) {
16066            
16067          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16068         
16069         this.footer.dataSource = this.store;
16070         this.footer.container = fctr;
16071         this.footer = Roo.factory(this.footer, Roo);
16072         fctr.insertFirst(this.el);
16073         
16074         // this is a bit insane - as the paging toolbar seems to detach the el..
16075 //        dom.parentNode.parentNode.parentNode
16076          // they get detached?
16077     }
16078     
16079     
16080     Roo.View.superclass.constructor.call(this);
16081     
16082     
16083 };
16084
16085 Roo.extend(Roo.View, Roo.util.Observable, {
16086     
16087      /**
16088      * @cfg {Roo.data.Store} store Data store to load data from.
16089      */
16090     store : false,
16091     
16092     /**
16093      * @cfg {String|Roo.Element} el The container element.
16094      */
16095     el : '',
16096     
16097     /**
16098      * @cfg {String|Roo.Template} tpl The template used by this View 
16099      */
16100     tpl : false,
16101     /**
16102      * @cfg {String} dataName the named area of the template to use as the data area
16103      *                          Works with domtemplates roo-name="name"
16104      */
16105     dataName: false,
16106     /**
16107      * @cfg {String} selectedClass The css class to add to selected nodes
16108      */
16109     selectedClass : "x-view-selected",
16110      /**
16111      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16112      */
16113     emptyText : "",
16114     
16115     /**
16116      * @cfg {String} text to display on mask (default Loading)
16117      */
16118     mask : false,
16119     /**
16120      * @cfg {Boolean} multiSelect Allow multiple selection
16121      */
16122     multiSelect : false,
16123     /**
16124      * @cfg {Boolean} singleSelect Allow single selection
16125      */
16126     singleSelect:  false,
16127     
16128     /**
16129      * @cfg {Boolean} toggleSelect - selecting 
16130      */
16131     toggleSelect : false,
16132     
16133     /**
16134      * @cfg {Boolean} tickable - selecting 
16135      */
16136     tickable : false,
16137     
16138     /**
16139      * Returns the element this view is bound to.
16140      * @return {Roo.Element}
16141      */
16142     getEl : function(){
16143         return this.wrapEl;
16144     },
16145     
16146     
16147
16148     /**
16149      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16150      */
16151     refresh : function(){
16152         //Roo.log('refresh');
16153         var t = this.tpl;
16154         
16155         // if we are using something like 'domtemplate', then
16156         // the what gets used is:
16157         // t.applySubtemplate(NAME, data, wrapping data..)
16158         // the outer template then get' applied with
16159         //     the store 'extra data'
16160         // and the body get's added to the
16161         //      roo-name="data" node?
16162         //      <span class='roo-tpl-{name}'></span> ?????
16163         
16164         
16165         
16166         this.clearSelections();
16167         this.el.update("");
16168         var html = [];
16169         var records = this.store.getRange();
16170         if(records.length < 1) {
16171             
16172             // is this valid??  = should it render a template??
16173             
16174             this.el.update(this.emptyText);
16175             return;
16176         }
16177         var el = this.el;
16178         if (this.dataName) {
16179             this.el.update(t.apply(this.store.meta)); //????
16180             el = this.el.child('.roo-tpl-' + this.dataName);
16181         }
16182         
16183         for(var i = 0, len = records.length; i < len; i++){
16184             var data = this.prepareData(records[i].data, i, records[i]);
16185             this.fireEvent("preparedata", this, data, i, records[i]);
16186             
16187             var d = Roo.apply({}, data);
16188             
16189             if(this.tickable){
16190                 Roo.apply(d, {'roo-id' : Roo.id()});
16191                 
16192                 var _this = this;
16193             
16194                 Roo.each(this.parent.item, function(item){
16195                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16196                         return;
16197                     }
16198                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16199                 });
16200             }
16201             
16202             html[html.length] = Roo.util.Format.trim(
16203                 this.dataName ?
16204                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16205                     t.apply(d)
16206             );
16207         }
16208         
16209         
16210         
16211         el.update(html.join(""));
16212         this.nodes = el.dom.childNodes;
16213         this.updateIndexes(0);
16214     },
16215     
16216
16217     /**
16218      * Function to override to reformat the data that is sent to
16219      * the template for each node.
16220      * DEPRICATED - use the preparedata event handler.
16221      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16222      * a JSON object for an UpdateManager bound view).
16223      */
16224     prepareData : function(data, index, record)
16225     {
16226         this.fireEvent("preparedata", this, data, index, record);
16227         return data;
16228     },
16229
16230     onUpdate : function(ds, record){
16231         // Roo.log('on update');   
16232         this.clearSelections();
16233         var index = this.store.indexOf(record);
16234         var n = this.nodes[index];
16235         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16236         n.parentNode.removeChild(n);
16237         this.updateIndexes(index, index);
16238     },
16239
16240     
16241     
16242 // --------- FIXME     
16243     onAdd : function(ds, records, index)
16244     {
16245         //Roo.log(['on Add', ds, records, index] );        
16246         this.clearSelections();
16247         if(this.nodes.length == 0){
16248             this.refresh();
16249             return;
16250         }
16251         var n = this.nodes[index];
16252         for(var i = 0, len = records.length; i < len; i++){
16253             var d = this.prepareData(records[i].data, i, records[i]);
16254             if(n){
16255                 this.tpl.insertBefore(n, d);
16256             }else{
16257                 
16258                 this.tpl.append(this.el, d);
16259             }
16260         }
16261         this.updateIndexes(index);
16262     },
16263
16264     onRemove : function(ds, record, index){
16265        // Roo.log('onRemove');
16266         this.clearSelections();
16267         var el = this.dataName  ?
16268             this.el.child('.roo-tpl-' + this.dataName) :
16269             this.el; 
16270         
16271         el.dom.removeChild(this.nodes[index]);
16272         this.updateIndexes(index);
16273     },
16274
16275     /**
16276      * Refresh an individual node.
16277      * @param {Number} index
16278      */
16279     refreshNode : function(index){
16280         this.onUpdate(this.store, this.store.getAt(index));
16281     },
16282
16283     updateIndexes : function(startIndex, endIndex){
16284         var ns = this.nodes;
16285         startIndex = startIndex || 0;
16286         endIndex = endIndex || ns.length - 1;
16287         for(var i = startIndex; i <= endIndex; i++){
16288             ns[i].nodeIndex = i;
16289         }
16290     },
16291
16292     /**
16293      * Changes the data store this view uses and refresh the view.
16294      * @param {Store} store
16295      */
16296     setStore : function(store, initial){
16297         if(!initial && this.store){
16298             this.store.un("datachanged", this.refresh);
16299             this.store.un("add", this.onAdd);
16300             this.store.un("remove", this.onRemove);
16301             this.store.un("update", this.onUpdate);
16302             this.store.un("clear", this.refresh);
16303             this.store.un("beforeload", this.onBeforeLoad);
16304             this.store.un("load", this.onLoad);
16305             this.store.un("loadexception", this.onLoad);
16306         }
16307         if(store){
16308           
16309             store.on("datachanged", this.refresh, this);
16310             store.on("add", this.onAdd, this);
16311             store.on("remove", this.onRemove, this);
16312             store.on("update", this.onUpdate, this);
16313             store.on("clear", this.refresh, this);
16314             store.on("beforeload", this.onBeforeLoad, this);
16315             store.on("load", this.onLoad, this);
16316             store.on("loadexception", this.onLoad, this);
16317         }
16318         
16319         if(store){
16320             this.refresh();
16321         }
16322     },
16323     /**
16324      * onbeforeLoad - masks the loading area.
16325      *
16326      */
16327     onBeforeLoad : function(store,opts)
16328     {
16329          //Roo.log('onBeforeLoad');   
16330         if (!opts.add) {
16331             this.el.update("");
16332         }
16333         this.el.mask(this.mask ? this.mask : "Loading" ); 
16334     },
16335     onLoad : function ()
16336     {
16337         this.el.unmask();
16338     },
16339     
16340
16341     /**
16342      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16343      * @param {HTMLElement} node
16344      * @return {HTMLElement} The template node
16345      */
16346     findItemFromChild : function(node){
16347         var el = this.dataName  ?
16348             this.el.child('.roo-tpl-' + this.dataName,true) :
16349             this.el.dom; 
16350         
16351         if(!node || node.parentNode == el){
16352                     return node;
16353             }
16354             var p = node.parentNode;
16355             while(p && p != el){
16356             if(p.parentNode == el){
16357                 return p;
16358             }
16359             p = p.parentNode;
16360         }
16361             return null;
16362     },
16363
16364     /** @ignore */
16365     onClick : function(e){
16366         var item = this.findItemFromChild(e.getTarget());
16367         if(item){
16368             var index = this.indexOf(item);
16369             if(this.onItemClick(item, index, e) !== false){
16370                 this.fireEvent("click", this, index, item, e);
16371             }
16372         }else{
16373             this.clearSelections();
16374         }
16375     },
16376
16377     /** @ignore */
16378     onContextMenu : function(e){
16379         var item = this.findItemFromChild(e.getTarget());
16380         if(item){
16381             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16382         }
16383     },
16384
16385     /** @ignore */
16386     onDblClick : function(e){
16387         var item = this.findItemFromChild(e.getTarget());
16388         if(item){
16389             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16390         }
16391     },
16392
16393     onItemClick : function(item, index, e)
16394     {
16395         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16396             return false;
16397         }
16398         if (this.toggleSelect) {
16399             var m = this.isSelected(item) ? 'unselect' : 'select';
16400             //Roo.log(m);
16401             var _t = this;
16402             _t[m](item, true, false);
16403             return true;
16404         }
16405         if(this.multiSelect || this.singleSelect){
16406             if(this.multiSelect && e.shiftKey && this.lastSelection){
16407                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16408             }else{
16409                 this.select(item, this.multiSelect && e.ctrlKey);
16410                 this.lastSelection = item;
16411             }
16412             
16413             if(!this.tickable){
16414                 e.preventDefault();
16415             }
16416             
16417         }
16418         return true;
16419     },
16420
16421     /**
16422      * Get the number of selected nodes.
16423      * @return {Number}
16424      */
16425     getSelectionCount : function(){
16426         return this.selections.length;
16427     },
16428
16429     /**
16430      * Get the currently selected nodes.
16431      * @return {Array} An array of HTMLElements
16432      */
16433     getSelectedNodes : function(){
16434         return this.selections;
16435     },
16436
16437     /**
16438      * Get the indexes of the selected nodes.
16439      * @return {Array}
16440      */
16441     getSelectedIndexes : function(){
16442         var indexes = [], s = this.selections;
16443         for(var i = 0, len = s.length; i < len; i++){
16444             indexes.push(s[i].nodeIndex);
16445         }
16446         return indexes;
16447     },
16448
16449     /**
16450      * Clear all selections
16451      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16452      */
16453     clearSelections : function(suppressEvent){
16454         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16455             this.cmp.elements = this.selections;
16456             this.cmp.removeClass(this.selectedClass);
16457             this.selections = [];
16458             if(!suppressEvent){
16459                 this.fireEvent("selectionchange", this, this.selections);
16460             }
16461         }
16462     },
16463
16464     /**
16465      * Returns true if the passed node is selected
16466      * @param {HTMLElement/Number} node The node or node index
16467      * @return {Boolean}
16468      */
16469     isSelected : function(node){
16470         var s = this.selections;
16471         if(s.length < 1){
16472             return false;
16473         }
16474         node = this.getNode(node);
16475         return s.indexOf(node) !== -1;
16476     },
16477
16478     /**
16479      * Selects nodes.
16480      * @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
16481      * @param {Boolean} keepExisting (optional) true to keep existing selections
16482      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16483      */
16484     select : function(nodeInfo, keepExisting, suppressEvent){
16485         if(nodeInfo instanceof Array){
16486             if(!keepExisting){
16487                 this.clearSelections(true);
16488             }
16489             for(var i = 0, len = nodeInfo.length; i < len; i++){
16490                 this.select(nodeInfo[i], true, true);
16491             }
16492             return;
16493         } 
16494         var node = this.getNode(nodeInfo);
16495         if(!node || this.isSelected(node)){
16496             return; // already selected.
16497         }
16498         if(!keepExisting){
16499             this.clearSelections(true);
16500         }
16501         
16502         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16503             Roo.fly(node).addClass(this.selectedClass);
16504             this.selections.push(node);
16505             if(!suppressEvent){
16506                 this.fireEvent("selectionchange", this, this.selections);
16507             }
16508         }
16509         
16510         
16511     },
16512       /**
16513      * Unselects nodes.
16514      * @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
16515      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16516      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16517      */
16518     unselect : function(nodeInfo, keepExisting, suppressEvent)
16519     {
16520         if(nodeInfo instanceof Array){
16521             Roo.each(this.selections, function(s) {
16522                 this.unselect(s, nodeInfo);
16523             }, this);
16524             return;
16525         }
16526         var node = this.getNode(nodeInfo);
16527         if(!node || !this.isSelected(node)){
16528             //Roo.log("not selected");
16529             return; // not selected.
16530         }
16531         // fireevent???
16532         var ns = [];
16533         Roo.each(this.selections, function(s) {
16534             if (s == node ) {
16535                 Roo.fly(node).removeClass(this.selectedClass);
16536
16537                 return;
16538             }
16539             ns.push(s);
16540         },this);
16541         
16542         this.selections= ns;
16543         this.fireEvent("selectionchange", this, this.selections);
16544     },
16545
16546     /**
16547      * Gets a template node.
16548      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16549      * @return {HTMLElement} The node or null if it wasn't found
16550      */
16551     getNode : function(nodeInfo){
16552         if(typeof nodeInfo == "string"){
16553             return document.getElementById(nodeInfo);
16554         }else if(typeof nodeInfo == "number"){
16555             return this.nodes[nodeInfo];
16556         }
16557         return nodeInfo;
16558     },
16559
16560     /**
16561      * Gets a range template nodes.
16562      * @param {Number} startIndex
16563      * @param {Number} endIndex
16564      * @return {Array} An array of nodes
16565      */
16566     getNodes : function(start, end){
16567         var ns = this.nodes;
16568         start = start || 0;
16569         end = typeof end == "undefined" ? ns.length - 1 : end;
16570         var nodes = [];
16571         if(start <= end){
16572             for(var i = start; i <= end; i++){
16573                 nodes.push(ns[i]);
16574             }
16575         } else{
16576             for(var i = start; i >= end; i--){
16577                 nodes.push(ns[i]);
16578             }
16579         }
16580         return nodes;
16581     },
16582
16583     /**
16584      * Finds the index of the passed node
16585      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16586      * @return {Number} The index of the node or -1
16587      */
16588     indexOf : function(node){
16589         node = this.getNode(node);
16590         if(typeof node.nodeIndex == "number"){
16591             return node.nodeIndex;
16592         }
16593         var ns = this.nodes;
16594         for(var i = 0, len = ns.length; i < len; i++){
16595             if(ns[i] == node){
16596                 return i;
16597             }
16598         }
16599         return -1;
16600     }
16601 });
16602 /*
16603  * - LGPL
16604  *
16605  * based on jquery fullcalendar
16606  * 
16607  */
16608
16609 Roo.bootstrap = Roo.bootstrap || {};
16610 /**
16611  * @class Roo.bootstrap.Calendar
16612  * @extends Roo.bootstrap.Component
16613  * Bootstrap Calendar class
16614  * @cfg {Boolean} loadMask (true|false) default false
16615  * @cfg {Object} header generate the user specific header of the calendar, default false
16616
16617  * @constructor
16618  * Create a new Container
16619  * @param {Object} config The config object
16620  */
16621
16622
16623
16624 Roo.bootstrap.Calendar = function(config){
16625     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16626      this.addEvents({
16627         /**
16628              * @event select
16629              * Fires when a date is selected
16630              * @param {DatePicker} this
16631              * @param {Date} date The selected date
16632              */
16633         'select': true,
16634         /**
16635              * @event monthchange
16636              * Fires when the displayed month changes 
16637              * @param {DatePicker} this
16638              * @param {Date} date The selected month
16639              */
16640         'monthchange': true,
16641         /**
16642              * @event evententer
16643              * Fires when mouse over an event
16644              * @param {Calendar} this
16645              * @param {event} Event
16646              */
16647         'evententer': true,
16648         /**
16649              * @event eventleave
16650              * Fires when the mouse leaves an
16651              * @param {Calendar} this
16652              * @param {event}
16653              */
16654         'eventleave': true,
16655         /**
16656              * @event eventclick
16657              * Fires when the mouse click an
16658              * @param {Calendar} this
16659              * @param {event}
16660              */
16661         'eventclick': true
16662         
16663     });
16664
16665 };
16666
16667 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16668     
16669      /**
16670      * @cfg {Number} startDay
16671      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16672      */
16673     startDay : 0,
16674     
16675     loadMask : false,
16676     
16677     header : false,
16678       
16679     getAutoCreate : function(){
16680         
16681         
16682         var fc_button = function(name, corner, style, content ) {
16683             return Roo.apply({},{
16684                 tag : 'span',
16685                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16686                          (corner.length ?
16687                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16688                             ''
16689                         ),
16690                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16691                 unselectable: 'on'
16692             });
16693         };
16694         
16695         var header = {};
16696         
16697         if(!this.header){
16698             header = {
16699                 tag : 'table',
16700                 cls : 'fc-header',
16701                 style : 'width:100%',
16702                 cn : [
16703                     {
16704                         tag: 'tr',
16705                         cn : [
16706                             {
16707                                 tag : 'td',
16708                                 cls : 'fc-header-left',
16709                                 cn : [
16710                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16711                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16712                                     { tag: 'span', cls: 'fc-header-space' },
16713                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16714
16715
16716                                 ]
16717                             },
16718
16719                             {
16720                                 tag : 'td',
16721                                 cls : 'fc-header-center',
16722                                 cn : [
16723                                     {
16724                                         tag: 'span',
16725                                         cls: 'fc-header-title',
16726                                         cn : {
16727                                             tag: 'H2',
16728                                             html : 'month / year'
16729                                         }
16730                                     }
16731
16732                                 ]
16733                             },
16734                             {
16735                                 tag : 'td',
16736                                 cls : 'fc-header-right',
16737                                 cn : [
16738                               /*      fc_button('month', 'left', '', 'month' ),
16739                                     fc_button('week', '', '', 'week' ),
16740                                     fc_button('day', 'right', '', 'day' )
16741                                 */    
16742
16743                                 ]
16744                             }
16745
16746                         ]
16747                     }
16748                 ]
16749             };
16750         }
16751         
16752         header = this.header;
16753         
16754        
16755         var cal_heads = function() {
16756             var ret = [];
16757             // fixme - handle this.
16758             
16759             for (var i =0; i < Date.dayNames.length; i++) {
16760                 var d = Date.dayNames[i];
16761                 ret.push({
16762                     tag: 'th',
16763                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16764                     html : d.substring(0,3)
16765                 });
16766                 
16767             }
16768             ret[0].cls += ' fc-first';
16769             ret[6].cls += ' fc-last';
16770             return ret;
16771         };
16772         var cal_cell = function(n) {
16773             return  {
16774                 tag: 'td',
16775                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16776                 cn : [
16777                     {
16778                         cn : [
16779                             {
16780                                 cls: 'fc-day-number',
16781                                 html: 'D'
16782                             },
16783                             {
16784                                 cls: 'fc-day-content',
16785                              
16786                                 cn : [
16787                                      {
16788                                         style: 'position: relative;' // height: 17px;
16789                                     }
16790                                 ]
16791                             }
16792                             
16793                             
16794                         ]
16795                     }
16796                 ]
16797                 
16798             }
16799         };
16800         var cal_rows = function() {
16801             
16802             var ret = [];
16803             for (var r = 0; r < 6; r++) {
16804                 var row= {
16805                     tag : 'tr',
16806                     cls : 'fc-week',
16807                     cn : []
16808                 };
16809                 
16810                 for (var i =0; i < Date.dayNames.length; i++) {
16811                     var d = Date.dayNames[i];
16812                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16813
16814                 }
16815                 row.cn[0].cls+=' fc-first';
16816                 row.cn[0].cn[0].style = 'min-height:90px';
16817                 row.cn[6].cls+=' fc-last';
16818                 ret.push(row);
16819                 
16820             }
16821             ret[0].cls += ' fc-first';
16822             ret[4].cls += ' fc-prev-last';
16823             ret[5].cls += ' fc-last';
16824             return ret;
16825             
16826         };
16827         
16828         var cal_table = {
16829             tag: 'table',
16830             cls: 'fc-border-separate',
16831             style : 'width:100%',
16832             cellspacing  : 0,
16833             cn : [
16834                 { 
16835                     tag: 'thead',
16836                     cn : [
16837                         { 
16838                             tag: 'tr',
16839                             cls : 'fc-first fc-last',
16840                             cn : cal_heads()
16841                         }
16842                     ]
16843                 },
16844                 { 
16845                     tag: 'tbody',
16846                     cn : cal_rows()
16847                 }
16848                   
16849             ]
16850         };
16851          
16852          var cfg = {
16853             cls : 'fc fc-ltr',
16854             cn : [
16855                 header,
16856                 {
16857                     cls : 'fc-content',
16858                     style : "position: relative;",
16859                     cn : [
16860                         {
16861                             cls : 'fc-view fc-view-month fc-grid',
16862                             style : 'position: relative',
16863                             unselectable : 'on',
16864                             cn : [
16865                                 {
16866                                     cls : 'fc-event-container',
16867                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16868                                 },
16869                                 cal_table
16870                             ]
16871                         }
16872                     ]
16873     
16874                 }
16875            ] 
16876             
16877         };
16878         
16879          
16880         
16881         return cfg;
16882     },
16883     
16884     
16885     initEvents : function()
16886     {
16887         if(!this.store){
16888             throw "can not find store for calendar";
16889         }
16890         
16891         var mark = {
16892             tag: "div",
16893             cls:"x-dlg-mask",
16894             style: "text-align:center",
16895             cn: [
16896                 {
16897                     tag: "div",
16898                     style: "background-color:white;width:50%;margin:250 auto",
16899                     cn: [
16900                         {
16901                             tag: "img",
16902                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16903                         },
16904                         {
16905                             tag: "span",
16906                             html: "Loading"
16907                         }
16908                         
16909                     ]
16910                 }
16911             ]
16912         };
16913         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16914         
16915         var size = this.el.select('.fc-content', true).first().getSize();
16916         this.maskEl.setSize(size.width, size.height);
16917         this.maskEl.enableDisplayMode("block");
16918         if(!this.loadMask){
16919             this.maskEl.hide();
16920         }
16921         
16922         this.store = Roo.factory(this.store, Roo.data);
16923         this.store.on('load', this.onLoad, this);
16924         this.store.on('beforeload', this.onBeforeLoad, this);
16925         
16926         this.resize();
16927         
16928         this.cells = this.el.select('.fc-day',true);
16929         //Roo.log(this.cells);
16930         this.textNodes = this.el.query('.fc-day-number');
16931         this.cells.addClassOnOver('fc-state-hover');
16932         
16933         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16934         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16935         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16936         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16937         
16938         this.on('monthchange', this.onMonthChange, this);
16939         
16940         this.update(new Date().clearTime());
16941     },
16942     
16943     resize : function() {
16944         var sz  = this.el.getSize();
16945         
16946         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16947         this.el.select('.fc-day-content div',true).setHeight(34);
16948     },
16949     
16950     
16951     // private
16952     showPrevMonth : function(e){
16953         this.update(this.activeDate.add("mo", -1));
16954     },
16955     showToday : function(e){
16956         this.update(new Date().clearTime());
16957     },
16958     // private
16959     showNextMonth : function(e){
16960         this.update(this.activeDate.add("mo", 1));
16961     },
16962
16963     // private
16964     showPrevYear : function(){
16965         this.update(this.activeDate.add("y", -1));
16966     },
16967
16968     // private
16969     showNextYear : function(){
16970         this.update(this.activeDate.add("y", 1));
16971     },
16972
16973     
16974    // private
16975     update : function(date)
16976     {
16977         var vd = this.activeDate;
16978         this.activeDate = date;
16979 //        if(vd && this.el){
16980 //            var t = date.getTime();
16981 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16982 //                Roo.log('using add remove');
16983 //                
16984 //                this.fireEvent('monthchange', this, date);
16985 //                
16986 //                this.cells.removeClass("fc-state-highlight");
16987 //                this.cells.each(function(c){
16988 //                   if(c.dateValue == t){
16989 //                       c.addClass("fc-state-highlight");
16990 //                       setTimeout(function(){
16991 //                            try{c.dom.firstChild.focus();}catch(e){}
16992 //                       }, 50);
16993 //                       return false;
16994 //                   }
16995 //                   return true;
16996 //                });
16997 //                return;
16998 //            }
16999 //        }
17000         
17001         var days = date.getDaysInMonth();
17002         
17003         var firstOfMonth = date.getFirstDateOfMonth();
17004         var startingPos = firstOfMonth.getDay()-this.startDay;
17005         
17006         if(startingPos < this.startDay){
17007             startingPos += 7;
17008         }
17009         
17010         var pm = date.add(Date.MONTH, -1);
17011         var prevStart = pm.getDaysInMonth()-startingPos;
17012 //        
17013         this.cells = this.el.select('.fc-day',true);
17014         this.textNodes = this.el.query('.fc-day-number');
17015         this.cells.addClassOnOver('fc-state-hover');
17016         
17017         var cells = this.cells.elements;
17018         var textEls = this.textNodes;
17019         
17020         Roo.each(cells, function(cell){
17021             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17022         });
17023         
17024         days += startingPos;
17025
17026         // convert everything to numbers so it's fast
17027         var day = 86400000;
17028         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17029         //Roo.log(d);
17030         //Roo.log(pm);
17031         //Roo.log(prevStart);
17032         
17033         var today = new Date().clearTime().getTime();
17034         var sel = date.clearTime().getTime();
17035         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17036         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17037         var ddMatch = this.disabledDatesRE;
17038         var ddText = this.disabledDatesText;
17039         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17040         var ddaysText = this.disabledDaysText;
17041         var format = this.format;
17042         
17043         var setCellClass = function(cal, cell){
17044             cell.row = 0;
17045             cell.events = [];
17046             cell.more = [];
17047             //Roo.log('set Cell Class');
17048             cell.title = "";
17049             var t = d.getTime();
17050             
17051             //Roo.log(d);
17052             
17053             cell.dateValue = t;
17054             if(t == today){
17055                 cell.className += " fc-today";
17056                 cell.className += " fc-state-highlight";
17057                 cell.title = cal.todayText;
17058             }
17059             if(t == sel){
17060                 // disable highlight in other month..
17061                 //cell.className += " fc-state-highlight";
17062                 
17063             }
17064             // disabling
17065             if(t < min) {
17066                 cell.className = " fc-state-disabled";
17067                 cell.title = cal.minText;
17068                 return;
17069             }
17070             if(t > max) {
17071                 cell.className = " fc-state-disabled";
17072                 cell.title = cal.maxText;
17073                 return;
17074             }
17075             if(ddays){
17076                 if(ddays.indexOf(d.getDay()) != -1){
17077                     cell.title = ddaysText;
17078                     cell.className = " fc-state-disabled";
17079                 }
17080             }
17081             if(ddMatch && format){
17082                 var fvalue = d.dateFormat(format);
17083                 if(ddMatch.test(fvalue)){
17084                     cell.title = ddText.replace("%0", fvalue);
17085                     cell.className = " fc-state-disabled";
17086                 }
17087             }
17088             
17089             if (!cell.initialClassName) {
17090                 cell.initialClassName = cell.dom.className;
17091             }
17092             
17093             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17094         };
17095
17096         var i = 0;
17097         
17098         for(; i < startingPos; i++) {
17099             textEls[i].innerHTML = (++prevStart);
17100             d.setDate(d.getDate()+1);
17101             
17102             cells[i].className = "fc-past fc-other-month";
17103             setCellClass(this, cells[i]);
17104         }
17105         
17106         var intDay = 0;
17107         
17108         for(; i < days; i++){
17109             intDay = i - startingPos + 1;
17110             textEls[i].innerHTML = (intDay);
17111             d.setDate(d.getDate()+1);
17112             
17113             cells[i].className = ''; // "x-date-active";
17114             setCellClass(this, cells[i]);
17115         }
17116         var extraDays = 0;
17117         
17118         for(; i < 42; i++) {
17119             textEls[i].innerHTML = (++extraDays);
17120             d.setDate(d.getDate()+1);
17121             
17122             cells[i].className = "fc-future fc-other-month";
17123             setCellClass(this, cells[i]);
17124         }
17125         
17126         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17127         
17128         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17129         
17130         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17131         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17132         
17133         if(totalRows != 6){
17134             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17135             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17136         }
17137         
17138         this.fireEvent('monthchange', this, date);
17139         
17140         
17141         /*
17142         if(!this.internalRender){
17143             var main = this.el.dom.firstChild;
17144             var w = main.offsetWidth;
17145             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17146             Roo.fly(main).setWidth(w);
17147             this.internalRender = true;
17148             // opera does not respect the auto grow header center column
17149             // then, after it gets a width opera refuses to recalculate
17150             // without a second pass
17151             if(Roo.isOpera && !this.secondPass){
17152                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17153                 this.secondPass = true;
17154                 this.update.defer(10, this, [date]);
17155             }
17156         }
17157         */
17158         
17159     },
17160     
17161     findCell : function(dt) {
17162         dt = dt.clearTime().getTime();
17163         var ret = false;
17164         this.cells.each(function(c){
17165             //Roo.log("check " +c.dateValue + '?=' + dt);
17166             if(c.dateValue == dt){
17167                 ret = c;
17168                 return false;
17169             }
17170             return true;
17171         });
17172         
17173         return ret;
17174     },
17175     
17176     findCells : function(ev) {
17177         var s = ev.start.clone().clearTime().getTime();
17178        // Roo.log(s);
17179         var e= ev.end.clone().clearTime().getTime();
17180        // Roo.log(e);
17181         var ret = [];
17182         this.cells.each(function(c){
17183              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17184             
17185             if(c.dateValue > e){
17186                 return ;
17187             }
17188             if(c.dateValue < s){
17189                 return ;
17190             }
17191             ret.push(c);
17192         });
17193         
17194         return ret;    
17195     },
17196     
17197 //    findBestRow: function(cells)
17198 //    {
17199 //        var ret = 0;
17200 //        
17201 //        for (var i =0 ; i < cells.length;i++) {
17202 //            ret  = Math.max(cells[i].rows || 0,ret);
17203 //        }
17204 //        return ret;
17205 //        
17206 //    },
17207     
17208     
17209     addItem : function(ev)
17210     {
17211         // look for vertical location slot in
17212         var cells = this.findCells(ev);
17213         
17214 //        ev.row = this.findBestRow(cells);
17215         
17216         // work out the location.
17217         
17218         var crow = false;
17219         var rows = [];
17220         for(var i =0; i < cells.length; i++) {
17221             
17222             cells[i].row = cells[0].row;
17223             
17224             if(i == 0){
17225                 cells[i].row = cells[i].row + 1;
17226             }
17227             
17228             if (!crow) {
17229                 crow = {
17230                     start : cells[i],
17231                     end :  cells[i]
17232                 };
17233                 continue;
17234             }
17235             if (crow.start.getY() == cells[i].getY()) {
17236                 // on same row.
17237                 crow.end = cells[i];
17238                 continue;
17239             }
17240             // different row.
17241             rows.push(crow);
17242             crow = {
17243                 start: cells[i],
17244                 end : cells[i]
17245             };
17246             
17247         }
17248         
17249         rows.push(crow);
17250         ev.els = [];
17251         ev.rows = rows;
17252         ev.cells = cells;
17253         
17254         cells[0].events.push(ev);
17255         
17256         this.calevents.push(ev);
17257     },
17258     
17259     clearEvents: function() {
17260         
17261         if(!this.calevents){
17262             return;
17263         }
17264         
17265         Roo.each(this.cells.elements, function(c){
17266             c.row = 0;
17267             c.events = [];
17268             c.more = [];
17269         });
17270         
17271         Roo.each(this.calevents, function(e) {
17272             Roo.each(e.els, function(el) {
17273                 el.un('mouseenter' ,this.onEventEnter, this);
17274                 el.un('mouseleave' ,this.onEventLeave, this);
17275                 el.remove();
17276             },this);
17277         },this);
17278         
17279         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17280             e.remove();
17281         });
17282         
17283     },
17284     
17285     renderEvents: function()
17286     {   
17287         var _this = this;
17288         
17289         this.cells.each(function(c) {
17290             
17291             if(c.row < 5){
17292                 return;
17293             }
17294             
17295             var ev = c.events;
17296             
17297             var r = 4;
17298             if(c.row != c.events.length){
17299                 r = 4 - (4 - (c.row - c.events.length));
17300             }
17301             
17302             c.events = ev.slice(0, r);
17303             c.more = ev.slice(r);
17304             
17305             if(c.more.length && c.more.length == 1){
17306                 c.events.push(c.more.pop());
17307             }
17308             
17309             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17310             
17311         });
17312             
17313         this.cells.each(function(c) {
17314             
17315             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17316             
17317             
17318             for (var e = 0; e < c.events.length; e++){
17319                 var ev = c.events[e];
17320                 var rows = ev.rows;
17321                 
17322                 for(var i = 0; i < rows.length; i++) {
17323                 
17324                     // how many rows should it span..
17325
17326                     var  cfg = {
17327                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17328                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17329
17330                         unselectable : "on",
17331                         cn : [
17332                             {
17333                                 cls: 'fc-event-inner',
17334                                 cn : [
17335     //                                {
17336     //                                  tag:'span',
17337     //                                  cls: 'fc-event-time',
17338     //                                  html : cells.length > 1 ? '' : ev.time
17339     //                                },
17340                                     {
17341                                       tag:'span',
17342                                       cls: 'fc-event-title',
17343                                       html : String.format('{0}', ev.title)
17344                                     }
17345
17346
17347                                 ]
17348                             },
17349                             {
17350                                 cls: 'ui-resizable-handle ui-resizable-e',
17351                                 html : '&nbsp;&nbsp;&nbsp'
17352                             }
17353
17354                         ]
17355                     };
17356
17357                     if (i == 0) {
17358                         cfg.cls += ' fc-event-start';
17359                     }
17360                     if ((i+1) == rows.length) {
17361                         cfg.cls += ' fc-event-end';
17362                     }
17363
17364                     var ctr = _this.el.select('.fc-event-container',true).first();
17365                     var cg = ctr.createChild(cfg);
17366
17367                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17368                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17369
17370                     var r = (c.more.length) ? 1 : 0;
17371                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17372                     cg.setWidth(ebox.right - sbox.x -2);
17373
17374                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17375                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17376                     cg.on('click', _this.onEventClick, _this, ev);
17377
17378                     ev.els.push(cg);
17379                     
17380                 }
17381                 
17382             }
17383             
17384             
17385             if(c.more.length){
17386                 var  cfg = {
17387                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17388                     style : 'position: absolute',
17389                     unselectable : "on",
17390                     cn : [
17391                         {
17392                             cls: 'fc-event-inner',
17393                             cn : [
17394                                 {
17395                                   tag:'span',
17396                                   cls: 'fc-event-title',
17397                                   html : 'More'
17398                                 }
17399
17400
17401                             ]
17402                         },
17403                         {
17404                             cls: 'ui-resizable-handle ui-resizable-e',
17405                             html : '&nbsp;&nbsp;&nbsp'
17406                         }
17407
17408                     ]
17409                 };
17410
17411                 var ctr = _this.el.select('.fc-event-container',true).first();
17412                 var cg = ctr.createChild(cfg);
17413
17414                 var sbox = c.select('.fc-day-content',true).first().getBox();
17415                 var ebox = c.select('.fc-day-content',true).first().getBox();
17416                 //Roo.log(cg);
17417                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17418                 cg.setWidth(ebox.right - sbox.x -2);
17419
17420                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17421                 
17422             }
17423             
17424         });
17425         
17426         
17427         
17428     },
17429     
17430     onEventEnter: function (e, el,event,d) {
17431         this.fireEvent('evententer', this, el, event);
17432     },
17433     
17434     onEventLeave: function (e, el,event,d) {
17435         this.fireEvent('eventleave', this, el, event);
17436     },
17437     
17438     onEventClick: function (e, el,event,d) {
17439         this.fireEvent('eventclick', this, el, event);
17440     },
17441     
17442     onMonthChange: function () {
17443         this.store.load();
17444     },
17445     
17446     onMoreEventClick: function(e, el, more)
17447     {
17448         var _this = this;
17449         
17450         this.calpopover.placement = 'right';
17451         this.calpopover.setTitle('More');
17452         
17453         this.calpopover.setContent('');
17454         
17455         var ctr = this.calpopover.el.select('.popover-content', true).first();
17456         
17457         Roo.each(more, function(m){
17458             var cfg = {
17459                 cls : 'fc-event-hori fc-event-draggable',
17460                 html : m.title
17461             };
17462             var cg = ctr.createChild(cfg);
17463             
17464             cg.on('click', _this.onEventClick, _this, m);
17465         });
17466         
17467         this.calpopover.show(el);
17468         
17469         
17470     },
17471     
17472     onLoad: function () 
17473     {   
17474         this.calevents = [];
17475         var cal = this;
17476         
17477         if(this.store.getCount() > 0){
17478             this.store.data.each(function(d){
17479                cal.addItem({
17480                     id : d.data.id,
17481                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17482                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17483                     time : d.data.start_time,
17484                     title : d.data.title,
17485                     description : d.data.description,
17486                     venue : d.data.venue
17487                 });
17488             });
17489         }
17490         
17491         this.renderEvents();
17492         
17493         if(this.calevents.length && this.loadMask){
17494             this.maskEl.hide();
17495         }
17496     },
17497     
17498     onBeforeLoad: function()
17499     {
17500         this.clearEvents();
17501         if(this.loadMask){
17502             this.maskEl.show();
17503         }
17504     }
17505 });
17506
17507  
17508  /*
17509  * - LGPL
17510  *
17511  * element
17512  * 
17513  */
17514
17515 /**
17516  * @class Roo.bootstrap.Popover
17517  * @extends Roo.bootstrap.Component
17518  * Bootstrap Popover class
17519  * @cfg {String} html contents of the popover   (or false to use children..)
17520  * @cfg {String} title of popover (or false to hide)
17521  * @cfg {String} placement how it is placed
17522  * @cfg {String} trigger click || hover (or false to trigger manually)
17523  * @cfg {String} over what (parent or false to trigger manually.)
17524  * @cfg {Number} delay - delay before showing
17525  
17526  * @constructor
17527  * Create a new Popover
17528  * @param {Object} config The config object
17529  */
17530
17531 Roo.bootstrap.Popover = function(config){
17532     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17533     
17534     this.addEvents({
17535         // raw events
17536          /**
17537          * @event show
17538          * After the popover show
17539          * 
17540          * @param {Roo.bootstrap.Popover} this
17541          */
17542         "show" : true,
17543         /**
17544          * @event hide
17545          * After the popover hide
17546          * 
17547          * @param {Roo.bootstrap.Popover} this
17548          */
17549         "hide" : true
17550     });
17551 };
17552
17553 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17554     
17555     title: 'Fill in a title',
17556     html: false,
17557     
17558     placement : 'right',
17559     trigger : 'hover', // hover
17560     
17561     delay : 0,
17562     
17563     over: 'parent',
17564     
17565     can_build_overlaid : false,
17566     
17567     getChildContainer : function()
17568     {
17569         return this.el.select('.popover-content',true).first();
17570     },
17571     
17572     getAutoCreate : function(){
17573          
17574         var cfg = {
17575            cls : 'popover roo-dynamic',
17576            style: 'display:block',
17577            cn : [
17578                 {
17579                     cls : 'arrow'
17580                 },
17581                 {
17582                     cls : 'popover-inner',
17583                     cn : [
17584                         {
17585                             tag: 'h3',
17586                             cls: 'popover-title',
17587                             html : this.title
17588                         },
17589                         {
17590                             cls : 'popover-content',
17591                             html : this.html
17592                         }
17593                     ]
17594                     
17595                 }
17596            ]
17597         };
17598         
17599         return cfg;
17600     },
17601     setTitle: function(str)
17602     {
17603         this.title = str;
17604         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17605     },
17606     setContent: function(str)
17607     {
17608         this.html = str;
17609         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17610     },
17611     // as it get's added to the bottom of the page.
17612     onRender : function(ct, position)
17613     {
17614         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17615         if(!this.el){
17616             var cfg = Roo.apply({},  this.getAutoCreate());
17617             cfg.id = Roo.id();
17618             
17619             if (this.cls) {
17620                 cfg.cls += ' ' + this.cls;
17621             }
17622             if (this.style) {
17623                 cfg.style = this.style;
17624             }
17625             //Roo.log("adding to ");
17626             this.el = Roo.get(document.body).createChild(cfg, position);
17627 //            Roo.log(this.el);
17628         }
17629         this.initEvents();
17630     },
17631     
17632     initEvents : function()
17633     {
17634         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17635         this.el.enableDisplayMode('block');
17636         this.el.hide();
17637         if (this.over === false) {
17638             return; 
17639         }
17640         if (this.triggers === false) {
17641             return;
17642         }
17643         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17644         var triggers = this.trigger ? this.trigger.split(' ') : [];
17645         Roo.each(triggers, function(trigger) {
17646         
17647             if (trigger == 'click') {
17648                 on_el.on('click', this.toggle, this);
17649             } else if (trigger != 'manual') {
17650                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17651                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17652       
17653                 on_el.on(eventIn  ,this.enter, this);
17654                 on_el.on(eventOut, this.leave, this);
17655             }
17656         }, this);
17657         
17658     },
17659     
17660     
17661     // private
17662     timeout : null,
17663     hoverState : null,
17664     
17665     toggle : function () {
17666         this.hoverState == 'in' ? this.leave() : this.enter();
17667     },
17668     
17669     enter : function () {
17670         
17671         clearTimeout(this.timeout);
17672     
17673         this.hoverState = 'in';
17674     
17675         if (!this.delay || !this.delay.show) {
17676             this.show();
17677             return;
17678         }
17679         var _t = this;
17680         this.timeout = setTimeout(function () {
17681             if (_t.hoverState == 'in') {
17682                 _t.show();
17683             }
17684         }, this.delay.show)
17685     },
17686     
17687     leave : function() {
17688         clearTimeout(this.timeout);
17689     
17690         this.hoverState = 'out';
17691     
17692         if (!this.delay || !this.delay.hide) {
17693             this.hide();
17694             return;
17695         }
17696         var _t = this;
17697         this.timeout = setTimeout(function () {
17698             if (_t.hoverState == 'out') {
17699                 _t.hide();
17700             }
17701         }, this.delay.hide)
17702     },
17703     
17704     show : function (on_el)
17705     {
17706         if (!on_el) {
17707             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17708         }
17709         
17710         // set content.
17711         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17712         if (this.html !== false) {
17713             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17714         }
17715         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17716         if (!this.title.length) {
17717             this.el.select('.popover-title',true).hide();
17718         }
17719         
17720         var placement = typeof this.placement == 'function' ?
17721             this.placement.call(this, this.el, on_el) :
17722             this.placement;
17723             
17724         var autoToken = /\s?auto?\s?/i;
17725         var autoPlace = autoToken.test(placement);
17726         if (autoPlace) {
17727             placement = placement.replace(autoToken, '') || 'top';
17728         }
17729         
17730         //this.el.detach()
17731         //this.el.setXY([0,0]);
17732         this.el.show();
17733         this.el.dom.style.display='block';
17734         this.el.addClass(placement);
17735         
17736         //this.el.appendTo(on_el);
17737         
17738         var p = this.getPosition();
17739         var box = this.el.getBox();
17740         
17741         if (autoPlace) {
17742             // fixme..
17743         }
17744         var align = Roo.bootstrap.Popover.alignment[placement];
17745         
17746 //        Roo.log(align);
17747         this.el.alignTo(on_el, align[0],align[1]);
17748         //var arrow = this.el.select('.arrow',true).first();
17749         //arrow.set(align[2], 
17750         
17751         this.el.addClass('in');
17752         
17753         
17754         if (this.el.hasClass('fade')) {
17755             // fade it?
17756         }
17757         
17758         this.hoverState = 'in';
17759         
17760         this.fireEvent('show', this);
17761         
17762     },
17763     hide : function()
17764     {
17765         this.el.setXY([0,0]);
17766         this.el.removeClass('in');
17767         this.el.hide();
17768         this.hoverState = null;
17769         
17770         this.fireEvent('hide', this);
17771     }
17772     
17773 });
17774
17775 Roo.bootstrap.Popover.alignment = {
17776     'left' : ['r-l', [-10,0], 'right'],
17777     'right' : ['l-r', [10,0], 'left'],
17778     'bottom' : ['t-b', [0,10], 'top'],
17779     'top' : [ 'b-t', [0,-10], 'bottom']
17780 };
17781
17782  /*
17783  * - LGPL
17784  *
17785  * Progress
17786  * 
17787  */
17788
17789 /**
17790  * @class Roo.bootstrap.Progress
17791  * @extends Roo.bootstrap.Component
17792  * Bootstrap Progress class
17793  * @cfg {Boolean} striped striped of the progress bar
17794  * @cfg {Boolean} active animated of the progress bar
17795  * 
17796  * 
17797  * @constructor
17798  * Create a new Progress
17799  * @param {Object} config The config object
17800  */
17801
17802 Roo.bootstrap.Progress = function(config){
17803     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17804 };
17805
17806 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17807     
17808     striped : false,
17809     active: false,
17810     
17811     getAutoCreate : function(){
17812         var cfg = {
17813             tag: 'div',
17814             cls: 'progress'
17815         };
17816         
17817         
17818         if(this.striped){
17819             cfg.cls += ' progress-striped';
17820         }
17821       
17822         if(this.active){
17823             cfg.cls += ' active';
17824         }
17825         
17826         
17827         return cfg;
17828     }
17829    
17830 });
17831
17832  
17833
17834  /*
17835  * - LGPL
17836  *
17837  * ProgressBar
17838  * 
17839  */
17840
17841 /**
17842  * @class Roo.bootstrap.ProgressBar
17843  * @extends Roo.bootstrap.Component
17844  * Bootstrap ProgressBar class
17845  * @cfg {Number} aria_valuenow aria-value now
17846  * @cfg {Number} aria_valuemin aria-value min
17847  * @cfg {Number} aria_valuemax aria-value max
17848  * @cfg {String} label label for the progress bar
17849  * @cfg {String} panel (success | info | warning | danger )
17850  * @cfg {String} role role of the progress bar
17851  * @cfg {String} sr_only text
17852  * 
17853  * 
17854  * @constructor
17855  * Create a new ProgressBar
17856  * @param {Object} config The config object
17857  */
17858
17859 Roo.bootstrap.ProgressBar = function(config){
17860     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17861 };
17862
17863 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17864     
17865     aria_valuenow : 0,
17866     aria_valuemin : 0,
17867     aria_valuemax : 100,
17868     label : false,
17869     panel : false,
17870     role : false,
17871     sr_only: false,
17872     
17873     getAutoCreate : function()
17874     {
17875         
17876         var cfg = {
17877             tag: 'div',
17878             cls: 'progress-bar',
17879             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17880         };
17881         
17882         if(this.sr_only){
17883             cfg.cn = {
17884                 tag: 'span',
17885                 cls: 'sr-only',
17886                 html: this.sr_only
17887             }
17888         }
17889         
17890         if(this.role){
17891             cfg.role = this.role;
17892         }
17893         
17894         if(this.aria_valuenow){
17895             cfg['aria-valuenow'] = this.aria_valuenow;
17896         }
17897         
17898         if(this.aria_valuemin){
17899             cfg['aria-valuemin'] = this.aria_valuemin;
17900         }
17901         
17902         if(this.aria_valuemax){
17903             cfg['aria-valuemax'] = this.aria_valuemax;
17904         }
17905         
17906         if(this.label && !this.sr_only){
17907             cfg.html = this.label;
17908         }
17909         
17910         if(this.panel){
17911             cfg.cls += ' progress-bar-' + this.panel;
17912         }
17913         
17914         return cfg;
17915     },
17916     
17917     update : function(aria_valuenow)
17918     {
17919         this.aria_valuenow = aria_valuenow;
17920         
17921         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17922     }
17923    
17924 });
17925
17926  
17927
17928  /*
17929  * - LGPL
17930  *
17931  * column
17932  * 
17933  */
17934
17935 /**
17936  * @class Roo.bootstrap.TabGroup
17937  * @extends Roo.bootstrap.Column
17938  * Bootstrap Column class
17939  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17940  * @cfg {Boolean} carousel true to make the group behave like a carousel
17941  * @cfg {Boolean} bullets show bullets for the panels
17942  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17943  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17944  * @cfg {Boolean} showarrow (true|false) show arrow default true
17945  * 
17946  * @constructor
17947  * Create a new TabGroup
17948  * @param {Object} config The config object
17949  */
17950
17951 Roo.bootstrap.TabGroup = function(config){
17952     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17953     if (!this.navId) {
17954         this.navId = Roo.id();
17955     }
17956     this.tabs = [];
17957     Roo.bootstrap.TabGroup.register(this);
17958     
17959 };
17960
17961 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17962     
17963     carousel : false,
17964     transition : false,
17965     bullets : 0,
17966     timer : 0,
17967     autoslide : false,
17968     slideFn : false,
17969     slideOnTouch : false,
17970     showarrow : true,
17971     
17972     getAutoCreate : function()
17973     {
17974         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17975         
17976         cfg.cls += ' tab-content';
17977         
17978         if (this.carousel) {
17979             cfg.cls += ' carousel slide';
17980             
17981             cfg.cn = [{
17982                cls : 'carousel-inner',
17983                cn : []
17984             }];
17985         
17986             if(this.bullets  && !Roo.isTouch){
17987                 
17988                 var bullets = {
17989                     cls : 'carousel-bullets',
17990                     cn : []
17991                 };
17992                
17993                 if(this.bullets_cls){
17994                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17995                 }
17996                 
17997                 bullets.cn.push({
17998                     cls : 'clear'
17999                 });
18000                 
18001                 cfg.cn[0].cn.push(bullets);
18002             }
18003             
18004             if(this.showarrow){
18005                 cfg.cn[0].cn.push({
18006                     tag : 'div',
18007                     class : 'carousel-arrow',
18008                     cn : [
18009                         {
18010                             tag : 'div',
18011                             class : 'carousel-prev',
18012                             cn : [
18013                                 {
18014                                     tag : 'i',
18015                                     class : 'fa fa-chevron-left'
18016                                 }
18017                             ]
18018                         },
18019                         {
18020                             tag : 'div',
18021                             class : 'carousel-next',
18022                             cn : [
18023                                 {
18024                                     tag : 'i',
18025                                     class : 'fa fa-chevron-right'
18026                                 }
18027                             ]
18028                         }
18029                     ]
18030                 });
18031             }
18032             
18033         }
18034         
18035         return cfg;
18036     },
18037     
18038     initEvents:  function()
18039     {
18040 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18041 //            this.el.on("touchstart", this.onTouchStart, this);
18042 //        }
18043         
18044         if(this.autoslide){
18045             var _this = this;
18046             
18047             this.slideFn = window.setInterval(function() {
18048                 _this.showPanelNext();
18049             }, this.timer);
18050         }
18051         
18052         if(this.showarrow){
18053             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18054             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18055         }
18056         
18057         
18058     },
18059     
18060 //    onTouchStart : function(e, el, o)
18061 //    {
18062 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18063 //            return;
18064 //        }
18065 //        
18066 //        this.showPanelNext();
18067 //    },
18068     
18069     
18070     getChildContainer : function()
18071     {
18072         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18073     },
18074     
18075     /**
18076     * register a Navigation item
18077     * @param {Roo.bootstrap.NavItem} the navitem to add
18078     */
18079     register : function(item)
18080     {
18081         this.tabs.push( item);
18082         item.navId = this.navId; // not really needed..
18083         this.addBullet();
18084     
18085     },
18086     
18087     getActivePanel : function()
18088     {
18089         var r = false;
18090         Roo.each(this.tabs, function(t) {
18091             if (t.active) {
18092                 r = t;
18093                 return false;
18094             }
18095             return null;
18096         });
18097         return r;
18098         
18099     },
18100     getPanelByName : function(n)
18101     {
18102         var r = false;
18103         Roo.each(this.tabs, function(t) {
18104             if (t.tabId == n) {
18105                 r = t;
18106                 return false;
18107             }
18108             return null;
18109         });
18110         return r;
18111     },
18112     indexOfPanel : function(p)
18113     {
18114         var r = false;
18115         Roo.each(this.tabs, function(t,i) {
18116             if (t.tabId == p.tabId) {
18117                 r = i;
18118                 return false;
18119             }
18120             return null;
18121         });
18122         return r;
18123     },
18124     /**
18125      * show a specific panel
18126      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18127      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18128      */
18129     showPanel : function (pan)
18130     {
18131         if(this.transition || typeof(pan) == 'undefined'){
18132             Roo.log("waiting for the transitionend");
18133             return;
18134         }
18135         
18136         if (typeof(pan) == 'number') {
18137             pan = this.tabs[pan];
18138         }
18139         
18140         if (typeof(pan) == 'string') {
18141             pan = this.getPanelByName(pan);
18142         }
18143         
18144         var cur = this.getActivePanel();
18145         
18146         if(!pan || !cur){
18147             Roo.log('pan or acitve pan is undefined');
18148             return false;
18149         }
18150         
18151         if (pan.tabId == this.getActivePanel().tabId) {
18152             return true;
18153         }
18154         
18155         if (false === cur.fireEvent('beforedeactivate')) {
18156             return false;
18157         }
18158         
18159         if(this.bullets > 0 && !Roo.isTouch){
18160             this.setActiveBullet(this.indexOfPanel(pan));
18161         }
18162         
18163         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18164             
18165             this.transition = true;
18166             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18167             var lr = dir == 'next' ? 'left' : 'right';
18168             pan.el.addClass(dir); // or prev
18169             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18170             cur.el.addClass(lr); // or right
18171             pan.el.addClass(lr);
18172             
18173             var _this = this;
18174             cur.el.on('transitionend', function() {
18175                 Roo.log("trans end?");
18176                 
18177                 pan.el.removeClass([lr,dir]);
18178                 pan.setActive(true);
18179                 
18180                 cur.el.removeClass([lr]);
18181                 cur.setActive(false);
18182                 
18183                 _this.transition = false;
18184                 
18185             }, this, { single:  true } );
18186             
18187             return true;
18188         }
18189         
18190         cur.setActive(false);
18191         pan.setActive(true);
18192         
18193         return true;
18194         
18195     },
18196     showPanelNext : function()
18197     {
18198         var i = this.indexOfPanel(this.getActivePanel());
18199         
18200         if (i >= this.tabs.length - 1 && !this.autoslide) {
18201             return;
18202         }
18203         
18204         if (i >= this.tabs.length - 1 && this.autoslide) {
18205             i = -1;
18206         }
18207         
18208         this.showPanel(this.tabs[i+1]);
18209     },
18210     
18211     showPanelPrev : function()
18212     {
18213         var i = this.indexOfPanel(this.getActivePanel());
18214         
18215         if (i  < 1 && !this.autoslide) {
18216             return;
18217         }
18218         
18219         if (i < 1 && this.autoslide) {
18220             i = this.tabs.length;
18221         }
18222         
18223         this.showPanel(this.tabs[i-1]);
18224     },
18225     
18226     
18227     addBullet: function()
18228     {
18229         if(!this.bullets || Roo.isTouch){
18230             return;
18231         }
18232         var ctr = this.el.select('.carousel-bullets',true).first();
18233         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18234         var bullet = ctr.createChild({
18235             cls : 'bullet bullet-' + i
18236         },ctr.dom.lastChild);
18237         
18238         
18239         var _this = this;
18240         
18241         bullet.on('click', (function(e, el, o, ii, t){
18242
18243             e.preventDefault();
18244
18245             this.showPanel(ii);
18246
18247             if(this.autoslide && this.slideFn){
18248                 clearInterval(this.slideFn);
18249                 this.slideFn = window.setInterval(function() {
18250                     _this.showPanelNext();
18251                 }, this.timer);
18252             }
18253
18254         }).createDelegate(this, [i, bullet], true));
18255                 
18256         
18257     },
18258      
18259     setActiveBullet : function(i)
18260     {
18261         if(Roo.isTouch){
18262             return;
18263         }
18264         
18265         Roo.each(this.el.select('.bullet', true).elements, function(el){
18266             el.removeClass('selected');
18267         });
18268
18269         var bullet = this.el.select('.bullet-' + i, true).first();
18270         
18271         if(!bullet){
18272             return;
18273         }
18274         
18275         bullet.addClass('selected');
18276     }
18277     
18278     
18279   
18280 });
18281
18282  
18283
18284  
18285  
18286 Roo.apply(Roo.bootstrap.TabGroup, {
18287     
18288     groups: {},
18289      /**
18290     * register a Navigation Group
18291     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18292     */
18293     register : function(navgrp)
18294     {
18295         this.groups[navgrp.navId] = navgrp;
18296         
18297     },
18298     /**
18299     * fetch a Navigation Group based on the navigation ID
18300     * if one does not exist , it will get created.
18301     * @param {string} the navgroup to add
18302     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18303     */
18304     get: function(navId) {
18305         if (typeof(this.groups[navId]) == 'undefined') {
18306             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18307         }
18308         return this.groups[navId] ;
18309     }
18310     
18311     
18312     
18313 });
18314
18315  /*
18316  * - LGPL
18317  *
18318  * TabPanel
18319  * 
18320  */
18321
18322 /**
18323  * @class Roo.bootstrap.TabPanel
18324  * @extends Roo.bootstrap.Component
18325  * Bootstrap TabPanel class
18326  * @cfg {Boolean} active panel active
18327  * @cfg {String} html panel content
18328  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18329  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18330  * @cfg {String} href click to link..
18331  * 
18332  * 
18333  * @constructor
18334  * Create a new TabPanel
18335  * @param {Object} config The config object
18336  */
18337
18338 Roo.bootstrap.TabPanel = function(config){
18339     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18340     this.addEvents({
18341         /**
18342              * @event changed
18343              * Fires when the active status changes
18344              * @param {Roo.bootstrap.TabPanel} this
18345              * @param {Boolean} state the new state
18346             
18347          */
18348         'changed': true,
18349         /**
18350              * @event beforedeactivate
18351              * Fires before a tab is de-activated - can be used to do validation on a form.
18352              * @param {Roo.bootstrap.TabPanel} this
18353              * @return {Boolean} false if there is an error
18354             
18355          */
18356         'beforedeactivate': true
18357      });
18358     
18359     this.tabId = this.tabId || Roo.id();
18360   
18361 };
18362
18363 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18364     
18365     active: false,
18366     html: false,
18367     tabId: false,
18368     navId : false,
18369     href : '',
18370     
18371     getAutoCreate : function(){
18372         var cfg = {
18373             tag: 'div',
18374             // item is needed for carousel - not sure if it has any effect otherwise
18375             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18376             html: this.html || ''
18377         };
18378         
18379         if(this.active){
18380             cfg.cls += ' active';
18381         }
18382         
18383         if(this.tabId){
18384             cfg.tabId = this.tabId;
18385         }
18386         
18387         
18388         return cfg;
18389     },
18390     
18391     initEvents:  function()
18392     {
18393         var p = this.parent();
18394         
18395         this.navId = this.navId || p.navId;
18396         
18397         if (typeof(this.navId) != 'undefined') {
18398             // not really needed.. but just in case.. parent should be a NavGroup.
18399             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18400             
18401             tg.register(this);
18402             
18403             var i = tg.tabs.length - 1;
18404             
18405             if(this.active && tg.bullets > 0 && i < tg.bullets){
18406                 tg.setActiveBullet(i);
18407             }
18408         }
18409         
18410         this.el.on('click', this.onClick, this);
18411         
18412         if(Roo.isTouch){
18413             this.el.on("touchstart", this.onTouchStart, this);
18414             this.el.on("touchmove", this.onTouchMove, this);
18415             this.el.on("touchend", this.onTouchEnd, this);
18416         }
18417         
18418     },
18419     
18420     onRender : function(ct, position)
18421     {
18422         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18423     },
18424     
18425     setActive : function(state)
18426     {
18427         Roo.log("panel - set active " + this.tabId + "=" + state);
18428         
18429         this.active = state;
18430         if (!state) {
18431             this.el.removeClass('active');
18432             
18433         } else  if (!this.el.hasClass('active')) {
18434             this.el.addClass('active');
18435         }
18436         
18437         this.fireEvent('changed', this, state);
18438     },
18439     
18440     onClick : function(e)
18441     {
18442         e.preventDefault();
18443         
18444         if(!this.href.length){
18445             return;
18446         }
18447         
18448         window.location.href = this.href;
18449     },
18450     
18451     startX : 0,
18452     startY : 0,
18453     endX : 0,
18454     endY : 0,
18455     swiping : false,
18456     
18457     onTouchStart : function(e)
18458     {
18459         this.swiping = false;
18460         
18461         this.startX = e.browserEvent.touches[0].clientX;
18462         this.startY = e.browserEvent.touches[0].clientY;
18463     },
18464     
18465     onTouchMove : function(e)
18466     {
18467         this.swiping = true;
18468         
18469         this.endX = e.browserEvent.touches[0].clientX;
18470         this.endY = e.browserEvent.touches[0].clientY;
18471     },
18472     
18473     onTouchEnd : function(e)
18474     {
18475         if(!this.swiping){
18476             this.onClick(e);
18477             return;
18478         }
18479         
18480         var tabGroup = this.parent();
18481         
18482         if(this.endX > this.startX){ // swiping right
18483             tabGroup.showPanelPrev();
18484             return;
18485         }
18486         
18487         if(this.startX > this.endX){ // swiping left
18488             tabGroup.showPanelNext();
18489             return;
18490         }
18491     }
18492     
18493     
18494 });
18495  
18496
18497  
18498
18499  /*
18500  * - LGPL
18501  *
18502  * DateField
18503  * 
18504  */
18505
18506 /**
18507  * @class Roo.bootstrap.DateField
18508  * @extends Roo.bootstrap.Input
18509  * Bootstrap DateField class
18510  * @cfg {Number} weekStart default 0
18511  * @cfg {String} viewMode default empty, (months|years)
18512  * @cfg {String} minViewMode default empty, (months|years)
18513  * @cfg {Number} startDate default -Infinity
18514  * @cfg {Number} endDate default Infinity
18515  * @cfg {Boolean} todayHighlight default false
18516  * @cfg {Boolean} todayBtn default false
18517  * @cfg {Boolean} calendarWeeks default false
18518  * @cfg {Object} daysOfWeekDisabled default empty
18519  * @cfg {Boolean} singleMode default false (true | false)
18520  * 
18521  * @cfg {Boolean} keyboardNavigation default true
18522  * @cfg {String} language default en
18523  * 
18524  * @constructor
18525  * Create a new DateField
18526  * @param {Object} config The config object
18527  */
18528
18529 Roo.bootstrap.DateField = function(config){
18530     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18531      this.addEvents({
18532             /**
18533              * @event show
18534              * Fires when this field show.
18535              * @param {Roo.bootstrap.DateField} this
18536              * @param {Mixed} date The date value
18537              */
18538             show : true,
18539             /**
18540              * @event show
18541              * Fires when this field hide.
18542              * @param {Roo.bootstrap.DateField} this
18543              * @param {Mixed} date The date value
18544              */
18545             hide : true,
18546             /**
18547              * @event select
18548              * Fires when select a date.
18549              * @param {Roo.bootstrap.DateField} this
18550              * @param {Mixed} date The date value
18551              */
18552             select : true,
18553             /**
18554              * @event beforeselect
18555              * Fires when before select a date.
18556              * @param {Roo.bootstrap.DateField} this
18557              * @param {Mixed} date The date value
18558              */
18559             beforeselect : true
18560         });
18561 };
18562
18563 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18564     
18565     /**
18566      * @cfg {String} format
18567      * The default date format string which can be overriden for localization support.  The format must be
18568      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18569      */
18570     format : "m/d/y",
18571     /**
18572      * @cfg {String} altFormats
18573      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18574      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18575      */
18576     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18577     
18578     weekStart : 0,
18579     
18580     viewMode : '',
18581     
18582     minViewMode : '',
18583     
18584     todayHighlight : false,
18585     
18586     todayBtn: false,
18587     
18588     language: 'en',
18589     
18590     keyboardNavigation: true,
18591     
18592     calendarWeeks: false,
18593     
18594     startDate: -Infinity,
18595     
18596     endDate: Infinity,
18597     
18598     daysOfWeekDisabled: [],
18599     
18600     _events: [],
18601     
18602     singleMode : false,
18603     
18604     UTCDate: function()
18605     {
18606         return new Date(Date.UTC.apply(Date, arguments));
18607     },
18608     
18609     UTCToday: function()
18610     {
18611         var today = new Date();
18612         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18613     },
18614     
18615     getDate: function() {
18616             var d = this.getUTCDate();
18617             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18618     },
18619     
18620     getUTCDate: function() {
18621             return this.date;
18622     },
18623     
18624     setDate: function(d) {
18625             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18626     },
18627     
18628     setUTCDate: function(d) {
18629             this.date = d;
18630             this.setValue(this.formatDate(this.date));
18631     },
18632         
18633     onRender: function(ct, position)
18634     {
18635         
18636         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18637         
18638         this.language = this.language || 'en';
18639         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18640         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18641         
18642         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18643         this.format = this.format || 'm/d/y';
18644         this.isInline = false;
18645         this.isInput = true;
18646         this.component = this.el.select('.add-on', true).first() || false;
18647         this.component = (this.component && this.component.length === 0) ? false : this.component;
18648         this.hasInput = this.component && this.inputEl().length;
18649         
18650         if (typeof(this.minViewMode === 'string')) {
18651             switch (this.minViewMode) {
18652                 case 'months':
18653                     this.minViewMode = 1;
18654                     break;
18655                 case 'years':
18656                     this.minViewMode = 2;
18657                     break;
18658                 default:
18659                     this.minViewMode = 0;
18660                     break;
18661             }
18662         }
18663         
18664         if (typeof(this.viewMode === 'string')) {
18665             switch (this.viewMode) {
18666                 case 'months':
18667                     this.viewMode = 1;
18668                     break;
18669                 case 'years':
18670                     this.viewMode = 2;
18671                     break;
18672                 default:
18673                     this.viewMode = 0;
18674                     break;
18675             }
18676         }
18677                 
18678         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18679         
18680 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18681         
18682         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18683         
18684         this.picker().on('mousedown', this.onMousedown, this);
18685         this.picker().on('click', this.onClick, this);
18686         
18687         this.picker().addClass('datepicker-dropdown');
18688         
18689         this.startViewMode = this.viewMode;
18690         
18691         if(this.singleMode){
18692             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18693                 v.setVisibilityMode(Roo.Element.DISPLAY);
18694                 v.hide();
18695             });
18696             
18697             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18698                 v.setStyle('width', '189px');
18699             });
18700         }
18701         
18702         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18703             if(!this.calendarWeeks){
18704                 v.remove();
18705                 return;
18706             }
18707             
18708             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18709             v.attr('colspan', function(i, val){
18710                 return parseInt(val) + 1;
18711             });
18712         });
18713                         
18714         
18715         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18716         
18717         this.setStartDate(this.startDate);
18718         this.setEndDate(this.endDate);
18719         
18720         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18721         
18722         this.fillDow();
18723         this.fillMonths();
18724         this.update();
18725         this.showMode();
18726         
18727         if(this.isInline) {
18728             this.showPopup();
18729         }
18730     },
18731     
18732     picker : function()
18733     {
18734         return this.pickerEl;
18735 //        return this.el.select('.datepicker', true).first();
18736     },
18737     
18738     fillDow: function()
18739     {
18740         var dowCnt = this.weekStart;
18741         
18742         var dow = {
18743             tag: 'tr',
18744             cn: [
18745                 
18746             ]
18747         };
18748         
18749         if(this.calendarWeeks){
18750             dow.cn.push({
18751                 tag: 'th',
18752                 cls: 'cw',
18753                 html: '&nbsp;'
18754             })
18755         }
18756         
18757         while (dowCnt < this.weekStart + 7) {
18758             dow.cn.push({
18759                 tag: 'th',
18760                 cls: 'dow',
18761                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18762             });
18763         }
18764         
18765         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18766     },
18767     
18768     fillMonths: function()
18769     {    
18770         var i = 0;
18771         var months = this.picker().select('>.datepicker-months td', true).first();
18772         
18773         months.dom.innerHTML = '';
18774         
18775         while (i < 12) {
18776             var month = {
18777                 tag: 'span',
18778                 cls: 'month',
18779                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18780             };
18781             
18782             months.createChild(month);
18783         }
18784         
18785     },
18786     
18787     update: function()
18788     {
18789         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;
18790         
18791         if (this.date < this.startDate) {
18792             this.viewDate = new Date(this.startDate);
18793         } else if (this.date > this.endDate) {
18794             this.viewDate = new Date(this.endDate);
18795         } else {
18796             this.viewDate = new Date(this.date);
18797         }
18798         
18799         this.fill();
18800     },
18801     
18802     fill: function() 
18803     {
18804         var d = new Date(this.viewDate),
18805                 year = d.getUTCFullYear(),
18806                 month = d.getUTCMonth(),
18807                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18808                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18809                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18810                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18811                 currentDate = this.date && this.date.valueOf(),
18812                 today = this.UTCToday();
18813         
18814         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18815         
18816 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18817         
18818 //        this.picker.select('>tfoot th.today').
18819 //                                              .text(dates[this.language].today)
18820 //                                              .toggle(this.todayBtn !== false);
18821     
18822         this.updateNavArrows();
18823         this.fillMonths();
18824                                                 
18825         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18826         
18827         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18828          
18829         prevMonth.setUTCDate(day);
18830         
18831         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18832         
18833         var nextMonth = new Date(prevMonth);
18834         
18835         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18836         
18837         nextMonth = nextMonth.valueOf();
18838         
18839         var fillMonths = false;
18840         
18841         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18842         
18843         while(prevMonth.valueOf() <= nextMonth) {
18844             var clsName = '';
18845             
18846             if (prevMonth.getUTCDay() === this.weekStart) {
18847                 if(fillMonths){
18848                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18849                 }
18850                     
18851                 fillMonths = {
18852                     tag: 'tr',
18853                     cn: []
18854                 };
18855                 
18856                 if(this.calendarWeeks){
18857                     // ISO 8601: First week contains first thursday.
18858                     // ISO also states week starts on Monday, but we can be more abstract here.
18859                     var
18860                     // Start of current week: based on weekstart/current date
18861                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18862                     // Thursday of this week
18863                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18864                     // First Thursday of year, year from thursday
18865                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18866                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18867                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18868                     
18869                     fillMonths.cn.push({
18870                         tag: 'td',
18871                         cls: 'cw',
18872                         html: calWeek
18873                     });
18874                 }
18875             }
18876             
18877             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18878                 clsName += ' old';
18879             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18880                 clsName += ' new';
18881             }
18882             if (this.todayHighlight &&
18883                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18884                 prevMonth.getUTCMonth() == today.getMonth() &&
18885                 prevMonth.getUTCDate() == today.getDate()) {
18886                 clsName += ' today';
18887             }
18888             
18889             if (currentDate && prevMonth.valueOf() === currentDate) {
18890                 clsName += ' active';
18891             }
18892             
18893             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18894                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18895                     clsName += ' disabled';
18896             }
18897             
18898             fillMonths.cn.push({
18899                 tag: 'td',
18900                 cls: 'day ' + clsName,
18901                 html: prevMonth.getDate()
18902             });
18903             
18904             prevMonth.setDate(prevMonth.getDate()+1);
18905         }
18906           
18907         var currentYear = this.date && this.date.getUTCFullYear();
18908         var currentMonth = this.date && this.date.getUTCMonth();
18909         
18910         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18911         
18912         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18913             v.removeClass('active');
18914             
18915             if(currentYear === year && k === currentMonth){
18916                 v.addClass('active');
18917             }
18918             
18919             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18920                 v.addClass('disabled');
18921             }
18922             
18923         });
18924         
18925         
18926         year = parseInt(year/10, 10) * 10;
18927         
18928         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18929         
18930         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18931         
18932         year -= 1;
18933         for (var i = -1; i < 11; i++) {
18934             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18935                 tag: 'span',
18936                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18937                 html: year
18938             });
18939             
18940             year += 1;
18941         }
18942     },
18943     
18944     showMode: function(dir) 
18945     {
18946         if (dir) {
18947             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18948         }
18949         
18950         Roo.each(this.picker().select('>div',true).elements, function(v){
18951             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18952             v.hide();
18953         });
18954         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18955     },
18956     
18957     place: function()
18958     {
18959         if(this.isInline) {
18960             return;
18961         }
18962         
18963         this.picker().removeClass(['bottom', 'top']);
18964         
18965         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18966             /*
18967              * place to the top of element!
18968              *
18969              */
18970             
18971             this.picker().addClass('top');
18972             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18973             
18974             return;
18975         }
18976         
18977         this.picker().addClass('bottom');
18978         
18979         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18980     },
18981     
18982     parseDate : function(value)
18983     {
18984         if(!value || value instanceof Date){
18985             return value;
18986         }
18987         var v = Date.parseDate(value, this.format);
18988         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18989             v = Date.parseDate(value, 'Y-m-d');
18990         }
18991         if(!v && this.altFormats){
18992             if(!this.altFormatsArray){
18993                 this.altFormatsArray = this.altFormats.split("|");
18994             }
18995             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18996                 v = Date.parseDate(value, this.altFormatsArray[i]);
18997             }
18998         }
18999         return v;
19000     },
19001     
19002     formatDate : function(date, fmt)
19003     {   
19004         return (!date || !(date instanceof Date)) ?
19005         date : date.dateFormat(fmt || this.format);
19006     },
19007     
19008     onFocus : function()
19009     {
19010         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19011         this.showPopup();
19012     },
19013     
19014     onBlur : function()
19015     {
19016         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19017         
19018         var d = this.inputEl().getValue();
19019         
19020         this.setValue(d);
19021                 
19022         this.hidePopup();
19023     },
19024     
19025     showPopup : function()
19026     {
19027         this.picker().show();
19028         this.update();
19029         this.place();
19030         
19031         this.fireEvent('showpopup', this, this.date);
19032     },
19033     
19034     hidePopup : function()
19035     {
19036         if(this.isInline) {
19037             return;
19038         }
19039         this.picker().hide();
19040         this.viewMode = this.startViewMode;
19041         this.showMode();
19042         
19043         this.fireEvent('hidepopup', this, this.date);
19044         
19045     },
19046     
19047     onMousedown: function(e)
19048     {
19049         e.stopPropagation();
19050         e.preventDefault();
19051     },
19052     
19053     keyup: function(e)
19054     {
19055         Roo.bootstrap.DateField.superclass.keyup.call(this);
19056         this.update();
19057     },
19058
19059     setValue: function(v)
19060     {
19061         if(this.fireEvent('beforeselect', this, v) !== false){
19062             var d = new Date(this.parseDate(v) ).clearTime();
19063         
19064             if(isNaN(d.getTime())){
19065                 this.date = this.viewDate = '';
19066                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19067                 return;
19068             }
19069
19070             v = this.formatDate(d);
19071
19072             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19073
19074             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19075
19076             this.update();
19077
19078             this.fireEvent('select', this, this.date);
19079         }
19080     },
19081     
19082     getValue: function()
19083     {
19084         return this.formatDate(this.date);
19085     },
19086     
19087     fireKey: function(e)
19088     {
19089         if (!this.picker().isVisible()){
19090             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19091                 this.showPopup();
19092             }
19093             return;
19094         }
19095         
19096         var dateChanged = false,
19097         dir, day, month,
19098         newDate, newViewDate;
19099         
19100         switch(e.keyCode){
19101             case 27: // escape
19102                 this.hidePopup();
19103                 e.preventDefault();
19104                 break;
19105             case 37: // left
19106             case 39: // right
19107                 if (!this.keyboardNavigation) {
19108                     break;
19109                 }
19110                 dir = e.keyCode == 37 ? -1 : 1;
19111                 
19112                 if (e.ctrlKey){
19113                     newDate = this.moveYear(this.date, dir);
19114                     newViewDate = this.moveYear(this.viewDate, dir);
19115                 } else if (e.shiftKey){
19116                     newDate = this.moveMonth(this.date, dir);
19117                     newViewDate = this.moveMonth(this.viewDate, dir);
19118                 } else {
19119                     newDate = new Date(this.date);
19120                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19121                     newViewDate = new Date(this.viewDate);
19122                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19123                 }
19124                 if (this.dateWithinRange(newDate)){
19125                     this.date = newDate;
19126                     this.viewDate = newViewDate;
19127                     this.setValue(this.formatDate(this.date));
19128 //                    this.update();
19129                     e.preventDefault();
19130                     dateChanged = true;
19131                 }
19132                 break;
19133             case 38: // up
19134             case 40: // down
19135                 if (!this.keyboardNavigation) {
19136                     break;
19137                 }
19138                 dir = e.keyCode == 38 ? -1 : 1;
19139                 if (e.ctrlKey){
19140                     newDate = this.moveYear(this.date, dir);
19141                     newViewDate = this.moveYear(this.viewDate, dir);
19142                 } else if (e.shiftKey){
19143                     newDate = this.moveMonth(this.date, dir);
19144                     newViewDate = this.moveMonth(this.viewDate, dir);
19145                 } else {
19146                     newDate = new Date(this.date);
19147                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19148                     newViewDate = new Date(this.viewDate);
19149                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19150                 }
19151                 if (this.dateWithinRange(newDate)){
19152                     this.date = newDate;
19153                     this.viewDate = newViewDate;
19154                     this.setValue(this.formatDate(this.date));
19155 //                    this.update();
19156                     e.preventDefault();
19157                     dateChanged = true;
19158                 }
19159                 break;
19160             case 13: // enter
19161                 this.setValue(this.formatDate(this.date));
19162                 this.hidePopup();
19163                 e.preventDefault();
19164                 break;
19165             case 9: // tab
19166                 this.setValue(this.formatDate(this.date));
19167                 this.hidePopup();
19168                 break;
19169             case 16: // shift
19170             case 17: // ctrl
19171             case 18: // alt
19172                 break;
19173             default :
19174                 this.hidePopup();
19175                 
19176         }
19177     },
19178     
19179     
19180     onClick: function(e) 
19181     {
19182         e.stopPropagation();
19183         e.preventDefault();
19184         
19185         var target = e.getTarget();
19186         
19187         if(target.nodeName.toLowerCase() === 'i'){
19188             target = Roo.get(target).dom.parentNode;
19189         }
19190         
19191         var nodeName = target.nodeName;
19192         var className = target.className;
19193         var html = target.innerHTML;
19194         //Roo.log(nodeName);
19195         
19196         switch(nodeName.toLowerCase()) {
19197             case 'th':
19198                 switch(className) {
19199                     case 'switch':
19200                         this.showMode(1);
19201                         break;
19202                     case 'prev':
19203                     case 'next':
19204                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19205                         switch(this.viewMode){
19206                                 case 0:
19207                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19208                                         break;
19209                                 case 1:
19210                                 case 2:
19211                                         this.viewDate = this.moveYear(this.viewDate, dir);
19212                                         break;
19213                         }
19214                         this.fill();
19215                         break;
19216                     case 'today':
19217                         var date = new Date();
19218                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19219 //                        this.fill()
19220                         this.setValue(this.formatDate(this.date));
19221                         
19222                         this.hidePopup();
19223                         break;
19224                 }
19225                 break;
19226             case 'span':
19227                 if (className.indexOf('disabled') < 0) {
19228                     this.viewDate.setUTCDate(1);
19229                     if (className.indexOf('month') > -1) {
19230                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19231                     } else {
19232                         var year = parseInt(html, 10) || 0;
19233                         this.viewDate.setUTCFullYear(year);
19234                         
19235                     }
19236                     
19237                     if(this.singleMode){
19238                         this.setValue(this.formatDate(this.viewDate));
19239                         this.hidePopup();
19240                         return;
19241                     }
19242                     
19243                     this.showMode(-1);
19244                     this.fill();
19245                 }
19246                 break;
19247                 
19248             case 'td':
19249                 //Roo.log(className);
19250                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19251                     var day = parseInt(html, 10) || 1;
19252                     var year = this.viewDate.getUTCFullYear(),
19253                         month = this.viewDate.getUTCMonth();
19254
19255                     if (className.indexOf('old') > -1) {
19256                         if(month === 0 ){
19257                             month = 11;
19258                             year -= 1;
19259                         }else{
19260                             month -= 1;
19261                         }
19262                     } else if (className.indexOf('new') > -1) {
19263                         if (month == 11) {
19264                             month = 0;
19265                             year += 1;
19266                         } else {
19267                             month += 1;
19268                         }
19269                     }
19270                     //Roo.log([year,month,day]);
19271                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19272                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19273 //                    this.fill();
19274                     //Roo.log(this.formatDate(this.date));
19275                     this.setValue(this.formatDate(this.date));
19276                     this.hidePopup();
19277                 }
19278                 break;
19279         }
19280     },
19281     
19282     setStartDate: function(startDate)
19283     {
19284         this.startDate = startDate || -Infinity;
19285         if (this.startDate !== -Infinity) {
19286             this.startDate = this.parseDate(this.startDate);
19287         }
19288         this.update();
19289         this.updateNavArrows();
19290     },
19291
19292     setEndDate: function(endDate)
19293     {
19294         this.endDate = endDate || Infinity;
19295         if (this.endDate !== Infinity) {
19296             this.endDate = this.parseDate(this.endDate);
19297         }
19298         this.update();
19299         this.updateNavArrows();
19300     },
19301     
19302     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19303     {
19304         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19305         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19306             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19307         }
19308         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19309             return parseInt(d, 10);
19310         });
19311         this.update();
19312         this.updateNavArrows();
19313     },
19314     
19315     updateNavArrows: function() 
19316     {
19317         if(this.singleMode){
19318             return;
19319         }
19320         
19321         var d = new Date(this.viewDate),
19322         year = d.getUTCFullYear(),
19323         month = d.getUTCMonth();
19324         
19325         Roo.each(this.picker().select('.prev', true).elements, function(v){
19326             v.show();
19327             switch (this.viewMode) {
19328                 case 0:
19329
19330                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19331                         v.hide();
19332                     }
19333                     break;
19334                 case 1:
19335                 case 2:
19336                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19337                         v.hide();
19338                     }
19339                     break;
19340             }
19341         });
19342         
19343         Roo.each(this.picker().select('.next', true).elements, function(v){
19344             v.show();
19345             switch (this.viewMode) {
19346                 case 0:
19347
19348                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19349                         v.hide();
19350                     }
19351                     break;
19352                 case 1:
19353                 case 2:
19354                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19355                         v.hide();
19356                     }
19357                     break;
19358             }
19359         })
19360     },
19361     
19362     moveMonth: function(date, dir)
19363     {
19364         if (!dir) {
19365             return date;
19366         }
19367         var new_date = new Date(date.valueOf()),
19368         day = new_date.getUTCDate(),
19369         month = new_date.getUTCMonth(),
19370         mag = Math.abs(dir),
19371         new_month, test;
19372         dir = dir > 0 ? 1 : -1;
19373         if (mag == 1){
19374             test = dir == -1
19375             // If going back one month, make sure month is not current month
19376             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19377             ? function(){
19378                 return new_date.getUTCMonth() == month;
19379             }
19380             // If going forward one month, make sure month is as expected
19381             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19382             : function(){
19383                 return new_date.getUTCMonth() != new_month;
19384             };
19385             new_month = month + dir;
19386             new_date.setUTCMonth(new_month);
19387             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19388             if (new_month < 0 || new_month > 11) {
19389                 new_month = (new_month + 12) % 12;
19390             }
19391         } else {
19392             // For magnitudes >1, move one month at a time...
19393             for (var i=0; i<mag; i++) {
19394                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19395                 new_date = this.moveMonth(new_date, dir);
19396             }
19397             // ...then reset the day, keeping it in the new month
19398             new_month = new_date.getUTCMonth();
19399             new_date.setUTCDate(day);
19400             test = function(){
19401                 return new_month != new_date.getUTCMonth();
19402             };
19403         }
19404         // Common date-resetting loop -- if date is beyond end of month, make it
19405         // end of month
19406         while (test()){
19407             new_date.setUTCDate(--day);
19408             new_date.setUTCMonth(new_month);
19409         }
19410         return new_date;
19411     },
19412
19413     moveYear: function(date, dir)
19414     {
19415         return this.moveMonth(date, dir*12);
19416     },
19417
19418     dateWithinRange: function(date)
19419     {
19420         return date >= this.startDate && date <= this.endDate;
19421     },
19422
19423     
19424     remove: function() 
19425     {
19426         this.picker().remove();
19427     },
19428     
19429     validateValue : function(value)
19430     {
19431         if(this.getVisibilityEl().hasClass('hidden')){
19432             return true;
19433         }
19434         
19435         if(value.length < 1)  {
19436             if(this.allowBlank){
19437                 return true;
19438             }
19439             return false;
19440         }
19441         
19442         if(value.length < this.minLength){
19443             return false;
19444         }
19445         if(value.length > this.maxLength){
19446             return false;
19447         }
19448         if(this.vtype){
19449             var vt = Roo.form.VTypes;
19450             if(!vt[this.vtype](value, this)){
19451                 return false;
19452             }
19453         }
19454         if(typeof this.validator == "function"){
19455             var msg = this.validator(value);
19456             if(msg !== true){
19457                 return false;
19458             }
19459         }
19460         
19461         if(this.regex && !this.regex.test(value)){
19462             return false;
19463         }
19464         
19465         if(typeof(this.parseDate(value)) == 'undefined'){
19466             return false;
19467         }
19468         
19469         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19470             return false;
19471         }      
19472         
19473         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19474             return false;
19475         } 
19476         
19477         
19478         return true;
19479     },
19480     
19481     reset : function()
19482     {
19483         this.date = this.viewDate = '';
19484         
19485         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19486     }
19487    
19488 });
19489
19490 Roo.apply(Roo.bootstrap.DateField,  {
19491     
19492     head : {
19493         tag: 'thead',
19494         cn: [
19495         {
19496             tag: 'tr',
19497             cn: [
19498             {
19499                 tag: 'th',
19500                 cls: 'prev',
19501                 html: '<i class="fa fa-arrow-left"/>'
19502             },
19503             {
19504                 tag: 'th',
19505                 cls: 'switch',
19506                 colspan: '5'
19507             },
19508             {
19509                 tag: 'th',
19510                 cls: 'next',
19511                 html: '<i class="fa fa-arrow-right"/>'
19512             }
19513
19514             ]
19515         }
19516         ]
19517     },
19518     
19519     content : {
19520         tag: 'tbody',
19521         cn: [
19522         {
19523             tag: 'tr',
19524             cn: [
19525             {
19526                 tag: 'td',
19527                 colspan: '7'
19528             }
19529             ]
19530         }
19531         ]
19532     },
19533     
19534     footer : {
19535         tag: 'tfoot',
19536         cn: [
19537         {
19538             tag: 'tr',
19539             cn: [
19540             {
19541                 tag: 'th',
19542                 colspan: '7',
19543                 cls: 'today'
19544             }
19545                     
19546             ]
19547         }
19548         ]
19549     },
19550     
19551     dates:{
19552         en: {
19553             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19554             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19555             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19556             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19557             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19558             today: "Today"
19559         }
19560     },
19561     
19562     modes: [
19563     {
19564         clsName: 'days',
19565         navFnc: 'Month',
19566         navStep: 1
19567     },
19568     {
19569         clsName: 'months',
19570         navFnc: 'FullYear',
19571         navStep: 1
19572     },
19573     {
19574         clsName: 'years',
19575         navFnc: 'FullYear',
19576         navStep: 10
19577     }]
19578 });
19579
19580 Roo.apply(Roo.bootstrap.DateField,  {
19581   
19582     template : {
19583         tag: 'div',
19584         cls: 'datepicker dropdown-menu roo-dynamic',
19585         cn: [
19586         {
19587             tag: 'div',
19588             cls: 'datepicker-days',
19589             cn: [
19590             {
19591                 tag: 'table',
19592                 cls: 'table-condensed',
19593                 cn:[
19594                 Roo.bootstrap.DateField.head,
19595                 {
19596                     tag: 'tbody'
19597                 },
19598                 Roo.bootstrap.DateField.footer
19599                 ]
19600             }
19601             ]
19602         },
19603         {
19604             tag: 'div',
19605             cls: 'datepicker-months',
19606             cn: [
19607             {
19608                 tag: 'table',
19609                 cls: 'table-condensed',
19610                 cn:[
19611                 Roo.bootstrap.DateField.head,
19612                 Roo.bootstrap.DateField.content,
19613                 Roo.bootstrap.DateField.footer
19614                 ]
19615             }
19616             ]
19617         },
19618         {
19619             tag: 'div',
19620             cls: 'datepicker-years',
19621             cn: [
19622             {
19623                 tag: 'table',
19624                 cls: 'table-condensed',
19625                 cn:[
19626                 Roo.bootstrap.DateField.head,
19627                 Roo.bootstrap.DateField.content,
19628                 Roo.bootstrap.DateField.footer
19629                 ]
19630             }
19631             ]
19632         }
19633         ]
19634     }
19635 });
19636
19637  
19638
19639  /*
19640  * - LGPL
19641  *
19642  * TimeField
19643  * 
19644  */
19645
19646 /**
19647  * @class Roo.bootstrap.TimeField
19648  * @extends Roo.bootstrap.Input
19649  * Bootstrap DateField class
19650  * 
19651  * 
19652  * @constructor
19653  * Create a new TimeField
19654  * @param {Object} config The config object
19655  */
19656
19657 Roo.bootstrap.TimeField = function(config){
19658     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19659     this.addEvents({
19660             /**
19661              * @event show
19662              * Fires when this field show.
19663              * @param {Roo.bootstrap.DateField} thisthis
19664              * @param {Mixed} date The date value
19665              */
19666             show : true,
19667             /**
19668              * @event show
19669              * Fires when this field hide.
19670              * @param {Roo.bootstrap.DateField} this
19671              * @param {Mixed} date The date value
19672              */
19673             hide : true,
19674             /**
19675              * @event select
19676              * Fires when select a date.
19677              * @param {Roo.bootstrap.DateField} this
19678              * @param {Mixed} date The date value
19679              */
19680             select : true
19681         });
19682 };
19683
19684 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19685     
19686     /**
19687      * @cfg {String} format
19688      * The default time format string which can be overriden for localization support.  The format must be
19689      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19690      */
19691     format : "H:i",
19692        
19693     onRender: function(ct, position)
19694     {
19695         
19696         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19697                 
19698         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19699         
19700         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19701         
19702         this.pop = this.picker().select('>.datepicker-time',true).first();
19703         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19704         
19705         this.picker().on('mousedown', this.onMousedown, this);
19706         this.picker().on('click', this.onClick, this);
19707         
19708         this.picker().addClass('datepicker-dropdown');
19709     
19710         this.fillTime();
19711         this.update();
19712             
19713         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19714         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19715         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19716         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19717         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19718         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19719
19720     },
19721     
19722     fireKey: function(e){
19723         if (!this.picker().isVisible()){
19724             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19725                 this.show();
19726             }
19727             return;
19728         }
19729
19730         e.preventDefault();
19731         
19732         switch(e.keyCode){
19733             case 27: // escape
19734                 this.hide();
19735                 break;
19736             case 37: // left
19737             case 39: // right
19738                 this.onTogglePeriod();
19739                 break;
19740             case 38: // up
19741                 this.onIncrementMinutes();
19742                 break;
19743             case 40: // down
19744                 this.onDecrementMinutes();
19745                 break;
19746             case 13: // enter
19747             case 9: // tab
19748                 this.setTime();
19749                 break;
19750         }
19751     },
19752     
19753     onClick: function(e) {
19754         e.stopPropagation();
19755         e.preventDefault();
19756     },
19757     
19758     picker : function()
19759     {
19760         return this.el.select('.datepicker', true).first();
19761     },
19762     
19763     fillTime: function()
19764     {    
19765         var time = this.pop.select('tbody', true).first();
19766         
19767         time.dom.innerHTML = '';
19768         
19769         time.createChild({
19770             tag: 'tr',
19771             cn: [
19772                 {
19773                     tag: 'td',
19774                     cn: [
19775                         {
19776                             tag: 'a',
19777                             href: '#',
19778                             cls: 'btn',
19779                             cn: [
19780                                 {
19781                                     tag: 'span',
19782                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19783                                 }
19784                             ]
19785                         } 
19786                     ]
19787                 },
19788                 {
19789                     tag: 'td',
19790                     cls: 'separator'
19791                 },
19792                 {
19793                     tag: 'td',
19794                     cn: [
19795                         {
19796                             tag: 'a',
19797                             href: '#',
19798                             cls: 'btn',
19799                             cn: [
19800                                 {
19801                                     tag: 'span',
19802                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19803                                 }
19804                             ]
19805                         }
19806                     ]
19807                 },
19808                 {
19809                     tag: 'td',
19810                     cls: 'separator'
19811                 }
19812             ]
19813         });
19814         
19815         time.createChild({
19816             tag: 'tr',
19817             cn: [
19818                 {
19819                     tag: 'td',
19820                     cn: [
19821                         {
19822                             tag: 'span',
19823                             cls: 'timepicker-hour',
19824                             html: '00'
19825                         }  
19826                     ]
19827                 },
19828                 {
19829                     tag: 'td',
19830                     cls: 'separator',
19831                     html: ':'
19832                 },
19833                 {
19834                     tag: 'td',
19835                     cn: [
19836                         {
19837                             tag: 'span',
19838                             cls: 'timepicker-minute',
19839                             html: '00'
19840                         }  
19841                     ]
19842                 },
19843                 {
19844                     tag: 'td',
19845                     cls: 'separator'
19846                 },
19847                 {
19848                     tag: 'td',
19849                     cn: [
19850                         {
19851                             tag: 'button',
19852                             type: 'button',
19853                             cls: 'btn btn-primary period',
19854                             html: 'AM'
19855                             
19856                         }
19857                     ]
19858                 }
19859             ]
19860         });
19861         
19862         time.createChild({
19863             tag: 'tr',
19864             cn: [
19865                 {
19866                     tag: 'td',
19867                     cn: [
19868                         {
19869                             tag: 'a',
19870                             href: '#',
19871                             cls: 'btn',
19872                             cn: [
19873                                 {
19874                                     tag: 'span',
19875                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19876                                 }
19877                             ]
19878                         }
19879                     ]
19880                 },
19881                 {
19882                     tag: 'td',
19883                     cls: 'separator'
19884                 },
19885                 {
19886                     tag: 'td',
19887                     cn: [
19888                         {
19889                             tag: 'a',
19890                             href: '#',
19891                             cls: 'btn',
19892                             cn: [
19893                                 {
19894                                     tag: 'span',
19895                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19896                                 }
19897                             ]
19898                         }
19899                     ]
19900                 },
19901                 {
19902                     tag: 'td',
19903                     cls: 'separator'
19904                 }
19905             ]
19906         });
19907         
19908     },
19909     
19910     update: function()
19911     {
19912         
19913         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19914         
19915         this.fill();
19916     },
19917     
19918     fill: function() 
19919     {
19920         var hours = this.time.getHours();
19921         var minutes = this.time.getMinutes();
19922         var period = 'AM';
19923         
19924         if(hours > 11){
19925             period = 'PM';
19926         }
19927         
19928         if(hours == 0){
19929             hours = 12;
19930         }
19931         
19932         
19933         if(hours > 12){
19934             hours = hours - 12;
19935         }
19936         
19937         if(hours < 10){
19938             hours = '0' + hours;
19939         }
19940         
19941         if(minutes < 10){
19942             minutes = '0' + minutes;
19943         }
19944         
19945         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19946         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19947         this.pop.select('button', true).first().dom.innerHTML = period;
19948         
19949     },
19950     
19951     place: function()
19952     {   
19953         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19954         
19955         var cls = ['bottom'];
19956         
19957         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19958             cls.pop();
19959             cls.push('top');
19960         }
19961         
19962         cls.push('right');
19963         
19964         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19965             cls.pop();
19966             cls.push('left');
19967         }
19968         
19969         this.picker().addClass(cls.join('-'));
19970         
19971         var _this = this;
19972         
19973         Roo.each(cls, function(c){
19974             if(c == 'bottom'){
19975                 _this.picker().setTop(_this.inputEl().getHeight());
19976                 return;
19977             }
19978             if(c == 'top'){
19979                 _this.picker().setTop(0 - _this.picker().getHeight());
19980                 return;
19981             }
19982             
19983             if(c == 'left'){
19984                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19985                 return;
19986             }
19987             if(c == 'right'){
19988                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19989                 return;
19990             }
19991         });
19992         
19993     },
19994   
19995     onFocus : function()
19996     {
19997         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19998         this.show();
19999     },
20000     
20001     onBlur : function()
20002     {
20003         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20004         this.hide();
20005     },
20006     
20007     show : function()
20008     {
20009         this.picker().show();
20010         this.pop.show();
20011         this.update();
20012         this.place();
20013         
20014         this.fireEvent('show', this, this.date);
20015     },
20016     
20017     hide : function()
20018     {
20019         this.picker().hide();
20020         this.pop.hide();
20021         
20022         this.fireEvent('hide', this, this.date);
20023     },
20024     
20025     setTime : function()
20026     {
20027         this.hide();
20028         this.setValue(this.time.format(this.format));
20029         
20030         this.fireEvent('select', this, this.date);
20031         
20032         
20033     },
20034     
20035     onMousedown: function(e){
20036         e.stopPropagation();
20037         e.preventDefault();
20038     },
20039     
20040     onIncrementHours: function()
20041     {
20042         Roo.log('onIncrementHours');
20043         this.time = this.time.add(Date.HOUR, 1);
20044         this.update();
20045         
20046     },
20047     
20048     onDecrementHours: function()
20049     {
20050         Roo.log('onDecrementHours');
20051         this.time = this.time.add(Date.HOUR, -1);
20052         this.update();
20053     },
20054     
20055     onIncrementMinutes: function()
20056     {
20057         Roo.log('onIncrementMinutes');
20058         this.time = this.time.add(Date.MINUTE, 1);
20059         this.update();
20060     },
20061     
20062     onDecrementMinutes: function()
20063     {
20064         Roo.log('onDecrementMinutes');
20065         this.time = this.time.add(Date.MINUTE, -1);
20066         this.update();
20067     },
20068     
20069     onTogglePeriod: function()
20070     {
20071         Roo.log('onTogglePeriod');
20072         this.time = this.time.add(Date.HOUR, 12);
20073         this.update();
20074     }
20075     
20076    
20077 });
20078
20079 Roo.apply(Roo.bootstrap.TimeField,  {
20080     
20081     content : {
20082         tag: 'tbody',
20083         cn: [
20084             {
20085                 tag: 'tr',
20086                 cn: [
20087                 {
20088                     tag: 'td',
20089                     colspan: '7'
20090                 }
20091                 ]
20092             }
20093         ]
20094     },
20095     
20096     footer : {
20097         tag: 'tfoot',
20098         cn: [
20099             {
20100                 tag: 'tr',
20101                 cn: [
20102                 {
20103                     tag: 'th',
20104                     colspan: '7',
20105                     cls: '',
20106                     cn: [
20107                         {
20108                             tag: 'button',
20109                             cls: 'btn btn-info ok',
20110                             html: 'OK'
20111                         }
20112                     ]
20113                 }
20114
20115                 ]
20116             }
20117         ]
20118     }
20119 });
20120
20121 Roo.apply(Roo.bootstrap.TimeField,  {
20122   
20123     template : {
20124         tag: 'div',
20125         cls: 'datepicker dropdown-menu',
20126         cn: [
20127             {
20128                 tag: 'div',
20129                 cls: 'datepicker-time',
20130                 cn: [
20131                 {
20132                     tag: 'table',
20133                     cls: 'table-condensed',
20134                     cn:[
20135                     Roo.bootstrap.TimeField.content,
20136                     Roo.bootstrap.TimeField.footer
20137                     ]
20138                 }
20139                 ]
20140             }
20141         ]
20142     }
20143 });
20144
20145  
20146
20147  /*
20148  * - LGPL
20149  *
20150  * MonthField
20151  * 
20152  */
20153
20154 /**
20155  * @class Roo.bootstrap.MonthField
20156  * @extends Roo.bootstrap.Input
20157  * Bootstrap MonthField class
20158  * 
20159  * @cfg {String} language default en
20160  * 
20161  * @constructor
20162  * Create a new MonthField
20163  * @param {Object} config The config object
20164  */
20165
20166 Roo.bootstrap.MonthField = function(config){
20167     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20168     
20169     this.addEvents({
20170         /**
20171          * @event show
20172          * Fires when this field show.
20173          * @param {Roo.bootstrap.MonthField} this
20174          * @param {Mixed} date The date value
20175          */
20176         show : true,
20177         /**
20178          * @event show
20179          * Fires when this field hide.
20180          * @param {Roo.bootstrap.MonthField} this
20181          * @param {Mixed} date The date value
20182          */
20183         hide : true,
20184         /**
20185          * @event select
20186          * Fires when select a date.
20187          * @param {Roo.bootstrap.MonthField} this
20188          * @param {String} oldvalue The old value
20189          * @param {String} newvalue The new value
20190          */
20191         select : true
20192     });
20193 };
20194
20195 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20196     
20197     onRender: function(ct, position)
20198     {
20199         
20200         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20201         
20202         this.language = this.language || 'en';
20203         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20204         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20205         
20206         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20207         this.isInline = false;
20208         this.isInput = true;
20209         this.component = this.el.select('.add-on', true).first() || false;
20210         this.component = (this.component && this.component.length === 0) ? false : this.component;
20211         this.hasInput = this.component && this.inputEL().length;
20212         
20213         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20214         
20215         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20216         
20217         this.picker().on('mousedown', this.onMousedown, this);
20218         this.picker().on('click', this.onClick, this);
20219         
20220         this.picker().addClass('datepicker-dropdown');
20221         
20222         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20223             v.setStyle('width', '189px');
20224         });
20225         
20226         this.fillMonths();
20227         
20228         this.update();
20229         
20230         if(this.isInline) {
20231             this.show();
20232         }
20233         
20234     },
20235     
20236     setValue: function(v, suppressEvent)
20237     {   
20238         var o = this.getValue();
20239         
20240         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20241         
20242         this.update();
20243
20244         if(suppressEvent !== true){
20245             this.fireEvent('select', this, o, v);
20246         }
20247         
20248     },
20249     
20250     getValue: function()
20251     {
20252         return this.value;
20253     },
20254     
20255     onClick: function(e) 
20256     {
20257         e.stopPropagation();
20258         e.preventDefault();
20259         
20260         var target = e.getTarget();
20261         
20262         if(target.nodeName.toLowerCase() === 'i'){
20263             target = Roo.get(target).dom.parentNode;
20264         }
20265         
20266         var nodeName = target.nodeName;
20267         var className = target.className;
20268         var html = target.innerHTML;
20269         
20270         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20271             return;
20272         }
20273         
20274         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20275         
20276         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20277         
20278         this.hide();
20279                         
20280     },
20281     
20282     picker : function()
20283     {
20284         return this.pickerEl;
20285     },
20286     
20287     fillMonths: function()
20288     {    
20289         var i = 0;
20290         var months = this.picker().select('>.datepicker-months td', true).first();
20291         
20292         months.dom.innerHTML = '';
20293         
20294         while (i < 12) {
20295             var month = {
20296                 tag: 'span',
20297                 cls: 'month',
20298                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20299             };
20300             
20301             months.createChild(month);
20302         }
20303         
20304     },
20305     
20306     update: function()
20307     {
20308         var _this = this;
20309         
20310         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20311             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20312         }
20313         
20314         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20315             e.removeClass('active');
20316             
20317             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20318                 e.addClass('active');
20319             }
20320         })
20321     },
20322     
20323     place: function()
20324     {
20325         if(this.isInline) {
20326             return;
20327         }
20328         
20329         this.picker().removeClass(['bottom', 'top']);
20330         
20331         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20332             /*
20333              * place to the top of element!
20334              *
20335              */
20336             
20337             this.picker().addClass('top');
20338             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20339             
20340             return;
20341         }
20342         
20343         this.picker().addClass('bottom');
20344         
20345         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20346     },
20347     
20348     onFocus : function()
20349     {
20350         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20351         this.show();
20352     },
20353     
20354     onBlur : function()
20355     {
20356         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20357         
20358         var d = this.inputEl().getValue();
20359         
20360         this.setValue(d);
20361                 
20362         this.hide();
20363     },
20364     
20365     show : function()
20366     {
20367         this.picker().show();
20368         this.picker().select('>.datepicker-months', true).first().show();
20369         this.update();
20370         this.place();
20371         
20372         this.fireEvent('show', this, this.date);
20373     },
20374     
20375     hide : function()
20376     {
20377         if(this.isInline) {
20378             return;
20379         }
20380         this.picker().hide();
20381         this.fireEvent('hide', this, this.date);
20382         
20383     },
20384     
20385     onMousedown: function(e)
20386     {
20387         e.stopPropagation();
20388         e.preventDefault();
20389     },
20390     
20391     keyup: function(e)
20392     {
20393         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20394         this.update();
20395     },
20396
20397     fireKey: function(e)
20398     {
20399         if (!this.picker().isVisible()){
20400             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20401                 this.show();
20402             }
20403             return;
20404         }
20405         
20406         var dir;
20407         
20408         switch(e.keyCode){
20409             case 27: // escape
20410                 this.hide();
20411                 e.preventDefault();
20412                 break;
20413             case 37: // left
20414             case 39: // right
20415                 dir = e.keyCode == 37 ? -1 : 1;
20416                 
20417                 this.vIndex = this.vIndex + dir;
20418                 
20419                 if(this.vIndex < 0){
20420                     this.vIndex = 0;
20421                 }
20422                 
20423                 if(this.vIndex > 11){
20424                     this.vIndex = 11;
20425                 }
20426                 
20427                 if(isNaN(this.vIndex)){
20428                     this.vIndex = 0;
20429                 }
20430                 
20431                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20432                 
20433                 break;
20434             case 38: // up
20435             case 40: // down
20436                 
20437                 dir = e.keyCode == 38 ? -1 : 1;
20438                 
20439                 this.vIndex = this.vIndex + dir * 4;
20440                 
20441                 if(this.vIndex < 0){
20442                     this.vIndex = 0;
20443                 }
20444                 
20445                 if(this.vIndex > 11){
20446                     this.vIndex = 11;
20447                 }
20448                 
20449                 if(isNaN(this.vIndex)){
20450                     this.vIndex = 0;
20451                 }
20452                 
20453                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20454                 break;
20455                 
20456             case 13: // enter
20457                 
20458                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20459                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20460                 }
20461                 
20462                 this.hide();
20463                 e.preventDefault();
20464                 break;
20465             case 9: // tab
20466                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20467                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20468                 }
20469                 this.hide();
20470                 break;
20471             case 16: // shift
20472             case 17: // ctrl
20473             case 18: // alt
20474                 break;
20475             default :
20476                 this.hide();
20477                 
20478         }
20479     },
20480     
20481     remove: function() 
20482     {
20483         this.picker().remove();
20484     }
20485    
20486 });
20487
20488 Roo.apply(Roo.bootstrap.MonthField,  {
20489     
20490     content : {
20491         tag: 'tbody',
20492         cn: [
20493         {
20494             tag: 'tr',
20495             cn: [
20496             {
20497                 tag: 'td',
20498                 colspan: '7'
20499             }
20500             ]
20501         }
20502         ]
20503     },
20504     
20505     dates:{
20506         en: {
20507             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20508             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20509         }
20510     }
20511 });
20512
20513 Roo.apply(Roo.bootstrap.MonthField,  {
20514   
20515     template : {
20516         tag: 'div',
20517         cls: 'datepicker dropdown-menu roo-dynamic',
20518         cn: [
20519             {
20520                 tag: 'div',
20521                 cls: 'datepicker-months',
20522                 cn: [
20523                 {
20524                     tag: 'table',
20525                     cls: 'table-condensed',
20526                     cn:[
20527                         Roo.bootstrap.DateField.content
20528                     ]
20529                 }
20530                 ]
20531             }
20532         ]
20533     }
20534 });
20535
20536  
20537
20538  
20539  /*
20540  * - LGPL
20541  *
20542  * CheckBox
20543  * 
20544  */
20545
20546 /**
20547  * @class Roo.bootstrap.CheckBox
20548  * @extends Roo.bootstrap.Input
20549  * Bootstrap CheckBox class
20550  * 
20551  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20552  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20553  * @cfg {String} boxLabel The text that appears beside the checkbox
20554  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20555  * @cfg {Boolean} checked initnal the element
20556  * @cfg {Boolean} inline inline the element (default false)
20557  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20558  * @cfg {String} tooltip label tooltip
20559  * 
20560  * @constructor
20561  * Create a new CheckBox
20562  * @param {Object} config The config object
20563  */
20564
20565 Roo.bootstrap.CheckBox = function(config){
20566     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20567    
20568     this.addEvents({
20569         /**
20570         * @event check
20571         * Fires when the element is checked or unchecked.
20572         * @param {Roo.bootstrap.CheckBox} this This input
20573         * @param {Boolean} checked The new checked value
20574         */
20575        check : true,
20576        /**
20577         * @event click
20578         * Fires when the element is click.
20579         * @param {Roo.bootstrap.CheckBox} this This input
20580         */
20581        click : true
20582     });
20583     
20584 };
20585
20586 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20587   
20588     inputType: 'checkbox',
20589     inputValue: 1,
20590     valueOff: 0,
20591     boxLabel: false,
20592     checked: false,
20593     weight : false,
20594     inline: false,
20595     tooltip : '',
20596     
20597     getAutoCreate : function()
20598     {
20599         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20600         
20601         var id = Roo.id();
20602         
20603         var cfg = {};
20604         
20605         cfg.cls = 'form-group ' + this.inputType; //input-group
20606         
20607         if(this.inline){
20608             cfg.cls += ' ' + this.inputType + '-inline';
20609         }
20610         
20611         var input =  {
20612             tag: 'input',
20613             id : id,
20614             type : this.inputType,
20615             value : this.inputValue,
20616             cls : 'roo-' + this.inputType, //'form-box',
20617             placeholder : this.placeholder || ''
20618             
20619         };
20620         
20621         if(this.inputType != 'radio'){
20622             var hidden =  {
20623                 tag: 'input',
20624                 type : 'hidden',
20625                 cls : 'roo-hidden-value',
20626                 value : this.checked ? this.inputValue : this.valueOff
20627             };
20628         }
20629         
20630             
20631         if (this.weight) { // Validity check?
20632             cfg.cls += " " + this.inputType + "-" + this.weight;
20633         }
20634         
20635         if (this.disabled) {
20636             input.disabled=true;
20637         }
20638         
20639         if(this.checked){
20640             input.checked = this.checked;
20641         }
20642         
20643         if (this.name) {
20644             
20645             input.name = this.name;
20646             
20647             if(this.inputType != 'radio'){
20648                 hidden.name = this.name;
20649                 input.name = '_hidden_' + this.name;
20650             }
20651         }
20652         
20653         if (this.size) {
20654             input.cls += ' input-' + this.size;
20655         }
20656         
20657         var settings=this;
20658         
20659         ['xs','sm','md','lg'].map(function(size){
20660             if (settings[size]) {
20661                 cfg.cls += ' col-' + size + '-' + settings[size];
20662             }
20663         });
20664         
20665         var inputblock = input;
20666          
20667         if (this.before || this.after) {
20668             
20669             inputblock = {
20670                 cls : 'input-group',
20671                 cn :  [] 
20672             };
20673             
20674             if (this.before) {
20675                 inputblock.cn.push({
20676                     tag :'span',
20677                     cls : 'input-group-addon',
20678                     html : this.before
20679                 });
20680             }
20681             
20682             inputblock.cn.push(input);
20683             
20684             if(this.inputType != 'radio'){
20685                 inputblock.cn.push(hidden);
20686             }
20687             
20688             if (this.after) {
20689                 inputblock.cn.push({
20690                     tag :'span',
20691                     cls : 'input-group-addon',
20692                     html : this.after
20693                 });
20694             }
20695             
20696         }
20697         
20698         if (align ==='left' && this.fieldLabel.length) {
20699 //                Roo.log("left and has label");
20700             cfg.cn = [
20701                 {
20702                     tag: 'label',
20703                     'for' :  id,
20704                     cls : 'control-label',
20705                     html : this.fieldLabel
20706                 },
20707                 {
20708                     cls : "", 
20709                     cn: [
20710                         inputblock
20711                     ]
20712                 }
20713             ];
20714             
20715             if(this.labelWidth > 12){
20716                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20717             }
20718             
20719             if(this.labelWidth < 13 && this.labelmd == 0){
20720                 this.labelmd = this.labelWidth;
20721             }
20722             
20723             if(this.labellg > 0){
20724                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20725                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20726             }
20727             
20728             if(this.labelmd > 0){
20729                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20730                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20731             }
20732             
20733             if(this.labelsm > 0){
20734                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20735                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20736             }
20737             
20738             if(this.labelxs > 0){
20739                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20740                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20741             }
20742             
20743         } else if ( this.fieldLabel.length) {
20744 //                Roo.log(" label");
20745                 cfg.cn = [
20746                    
20747                     {
20748                         tag: this.boxLabel ? 'span' : 'label',
20749                         'for': id,
20750                         cls: 'control-label box-input-label',
20751                         //cls : 'input-group-addon',
20752                         html : this.fieldLabel
20753                     },
20754                     
20755                     inputblock
20756                     
20757                 ];
20758
20759         } else {
20760             
20761 //                Roo.log(" no label && no align");
20762                 cfg.cn = [  inputblock ] ;
20763                 
20764                 
20765         }
20766         
20767         if(this.boxLabel){
20768              var boxLabelCfg = {
20769                 tag: 'label',
20770                 //'for': id, // box label is handled by onclick - so no for...
20771                 cls: 'box-label',
20772                 html: this.boxLabel
20773             };
20774             
20775             if(this.tooltip){
20776                 boxLabelCfg.tooltip = this.tooltip;
20777             }
20778              
20779             cfg.cn.push(boxLabelCfg);
20780         }
20781         
20782         if(this.inputType != 'radio'){
20783             cfg.cn.push(hidden);
20784         }
20785         
20786         return cfg;
20787         
20788     },
20789     
20790     /**
20791      * return the real input element.
20792      */
20793     inputEl: function ()
20794     {
20795         return this.el.select('input.roo-' + this.inputType,true).first();
20796     },
20797     hiddenEl: function ()
20798     {
20799         return this.el.select('input.roo-hidden-value',true).first();
20800     },
20801     
20802     labelEl: function()
20803     {
20804         return this.el.select('label.control-label',true).first();
20805     },
20806     /* depricated... */
20807     
20808     label: function()
20809     {
20810         return this.labelEl();
20811     },
20812     
20813     boxLabelEl: function()
20814     {
20815         return this.el.select('label.box-label',true).first();
20816     },
20817     
20818     initEvents : function()
20819     {
20820 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20821         
20822         this.inputEl().on('click', this.onClick,  this);
20823         
20824         if (this.boxLabel) { 
20825             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20826         }
20827         
20828         this.startValue = this.getValue();
20829         
20830         if(this.groupId){
20831             Roo.bootstrap.CheckBox.register(this);
20832         }
20833     },
20834     
20835     onClick : function(e)
20836     {   
20837         if(this.fireEvent('click', this, e) !== false){
20838             this.setChecked(!this.checked);
20839         }
20840         
20841     },
20842     
20843     setChecked : function(state,suppressEvent)
20844     {
20845         this.startValue = this.getValue();
20846
20847         if(this.inputType == 'radio'){
20848             
20849             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20850                 e.dom.checked = false;
20851             });
20852             
20853             this.inputEl().dom.checked = true;
20854             
20855             this.inputEl().dom.value = this.inputValue;
20856             
20857             if(suppressEvent !== true){
20858                 this.fireEvent('check', this, true);
20859             }
20860             
20861             this.validate();
20862             
20863             return;
20864         }
20865         
20866         this.checked = state;
20867         
20868         this.inputEl().dom.checked = state;
20869         
20870         
20871         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20872         
20873         if(suppressEvent !== true){
20874             this.fireEvent('check', this, state);
20875         }
20876         
20877         this.validate();
20878     },
20879     
20880     getValue : function()
20881     {
20882         if(this.inputType == 'radio'){
20883             return this.getGroupValue();
20884         }
20885         
20886         return this.hiddenEl().dom.value;
20887         
20888     },
20889     
20890     getGroupValue : function()
20891     {
20892         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20893             return '';
20894         }
20895         
20896         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20897     },
20898     
20899     setValue : function(v,suppressEvent)
20900     {
20901         if(this.inputType == 'radio'){
20902             this.setGroupValue(v, suppressEvent);
20903             return;
20904         }
20905         
20906         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20907         
20908         this.validate();
20909     },
20910     
20911     setGroupValue : function(v, suppressEvent)
20912     {
20913         this.startValue = this.getValue();
20914         
20915         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20916             e.dom.checked = false;
20917             
20918             if(e.dom.value == v){
20919                 e.dom.checked = true;
20920             }
20921         });
20922         
20923         if(suppressEvent !== true){
20924             this.fireEvent('check', this, true);
20925         }
20926
20927         this.validate();
20928         
20929         return;
20930     },
20931     
20932     validate : function()
20933     {
20934         if(this.getVisibilityEl().hasClass('hidden')){
20935             return true;
20936         }
20937         
20938         if(
20939                 this.disabled || 
20940                 (this.inputType == 'radio' && this.validateRadio()) ||
20941                 (this.inputType == 'checkbox' && this.validateCheckbox())
20942         ){
20943             this.markValid();
20944             return true;
20945         }
20946         
20947         this.markInvalid();
20948         return false;
20949     },
20950     
20951     validateRadio : function()
20952     {
20953         if(this.getVisibilityEl().hasClass('hidden')){
20954             return true;
20955         }
20956         
20957         if(this.allowBlank){
20958             return true;
20959         }
20960         
20961         var valid = false;
20962         
20963         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964             if(!e.dom.checked){
20965                 return;
20966             }
20967             
20968             valid = true;
20969             
20970             return false;
20971         });
20972         
20973         return valid;
20974     },
20975     
20976     validateCheckbox : function()
20977     {
20978         if(!this.groupId){
20979             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20980             //return (this.getValue() == this.inputValue) ? true : false;
20981         }
20982         
20983         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20984         
20985         if(!group){
20986             return false;
20987         }
20988         
20989         var r = false;
20990         
20991         for(var i in group){
20992             if(group[i].el.isVisible(true)){
20993                 r = false;
20994                 break;
20995             }
20996             
20997             r = true;
20998         }
20999         
21000         for(var i in group){
21001             if(r){
21002                 break;
21003             }
21004             
21005             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21006         }
21007         
21008         return r;
21009     },
21010     
21011     /**
21012      * Mark this field as valid
21013      */
21014     markValid : function()
21015     {
21016         var _this = this;
21017         
21018         this.fireEvent('valid', this);
21019         
21020         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21021         
21022         if(this.groupId){
21023             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21024         }
21025         
21026         if(label){
21027             label.markValid();
21028         }
21029
21030         if(this.inputType == 'radio'){
21031             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21032                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21033                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21034             });
21035             
21036             return;
21037         }
21038
21039         if(!this.groupId){
21040             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21041             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21042             return;
21043         }
21044         
21045         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21046         
21047         if(!group){
21048             return;
21049         }
21050         
21051         for(var i in group){
21052             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21053             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21054         }
21055     },
21056     
21057      /**
21058      * Mark this field as invalid
21059      * @param {String} msg The validation message
21060      */
21061     markInvalid : function(msg)
21062     {
21063         if(this.allowBlank){
21064             return;
21065         }
21066         
21067         var _this = this;
21068         
21069         this.fireEvent('invalid', this, msg);
21070         
21071         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21072         
21073         if(this.groupId){
21074             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21075         }
21076         
21077         if(label){
21078             label.markInvalid();
21079         }
21080             
21081         if(this.inputType == 'radio'){
21082             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21083                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21084                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21085             });
21086             
21087             return;
21088         }
21089         
21090         if(!this.groupId){
21091             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21092             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21093             return;
21094         }
21095         
21096         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21097         
21098         if(!group){
21099             return;
21100         }
21101         
21102         for(var i in group){
21103             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21104             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21105         }
21106         
21107     },
21108     
21109     clearInvalid : function()
21110     {
21111         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21112         
21113         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21114         
21115         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21116         
21117         if (label && label.iconEl) {
21118             label.iconEl.removeClass(label.validClass);
21119             label.iconEl.removeClass(label.invalidClass);
21120         }
21121     },
21122     
21123     disable : function()
21124     {
21125         if(this.inputType != 'radio'){
21126             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21127             return;
21128         }
21129         
21130         var _this = this;
21131         
21132         if(this.rendered){
21133             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21134                 _this.getActionEl().addClass(this.disabledClass);
21135                 e.dom.disabled = true;
21136             });
21137         }
21138         
21139         this.disabled = true;
21140         this.fireEvent("disable", this);
21141         return this;
21142     },
21143
21144     enable : function()
21145     {
21146         if(this.inputType != 'radio'){
21147             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21148             return;
21149         }
21150         
21151         var _this = this;
21152         
21153         if(this.rendered){
21154             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21155                 _this.getActionEl().removeClass(this.disabledClass);
21156                 e.dom.disabled = false;
21157             });
21158         }
21159         
21160         this.disabled = false;
21161         this.fireEvent("enable", this);
21162         return this;
21163     },
21164     
21165     setBoxLabel : function(v)
21166     {
21167         this.boxLabel = v;
21168         
21169         if(this.rendered){
21170             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21171         }
21172     }
21173
21174 });
21175
21176 Roo.apply(Roo.bootstrap.CheckBox, {
21177     
21178     groups: {},
21179     
21180      /**
21181     * register a CheckBox Group
21182     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21183     */
21184     register : function(checkbox)
21185     {
21186         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21187             this.groups[checkbox.groupId] = {};
21188         }
21189         
21190         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21191             return;
21192         }
21193         
21194         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21195         
21196     },
21197     /**
21198     * fetch a CheckBox Group based on the group ID
21199     * @param {string} the group ID
21200     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21201     */
21202     get: function(groupId) {
21203         if (typeof(this.groups[groupId]) == 'undefined') {
21204             return false;
21205         }
21206         
21207         return this.groups[groupId] ;
21208     }
21209     
21210     
21211 });
21212 /*
21213  * - LGPL
21214  *
21215  * RadioItem
21216  * 
21217  */
21218
21219 /**
21220  * @class Roo.bootstrap.Radio
21221  * @extends Roo.bootstrap.Component
21222  * Bootstrap Radio class
21223  * @cfg {String} boxLabel - the label associated
21224  * @cfg {String} value - the value of radio
21225  * 
21226  * @constructor
21227  * Create a new Radio
21228  * @param {Object} config The config object
21229  */
21230 Roo.bootstrap.Radio = function(config){
21231     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21232     
21233 };
21234
21235 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21236     
21237     boxLabel : '',
21238     
21239     value : '',
21240     
21241     getAutoCreate : function()
21242     {
21243         var cfg = {
21244             tag : 'div',
21245             cls : 'form-group radio',
21246             cn : [
21247                 {
21248                     tag : 'label',
21249                     cls : 'box-label',
21250                     html : this.boxLabel
21251                 }
21252             ]
21253         };
21254         
21255         return cfg;
21256     },
21257     
21258     initEvents : function() 
21259     {
21260         this.parent().register(this);
21261         
21262         this.el.on('click', this.onClick, this);
21263         
21264     },
21265     
21266     onClick : function(e)
21267     {
21268         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21269             this.setChecked(true);
21270         }
21271     },
21272     
21273     setChecked : function(state, suppressEvent)
21274     {
21275         this.parent().setValue(this.value, suppressEvent);
21276         
21277     },
21278     
21279     setBoxLabel : function(v)
21280     {
21281         this.boxLabel = v;
21282         
21283         if(this.rendered){
21284             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21285         }
21286     }
21287     
21288 });
21289  
21290
21291  /*
21292  * - LGPL
21293  *
21294  * Input
21295  * 
21296  */
21297
21298 /**
21299  * @class Roo.bootstrap.SecurePass
21300  * @extends Roo.bootstrap.Input
21301  * Bootstrap SecurePass class
21302  *
21303  * 
21304  * @constructor
21305  * Create a new SecurePass
21306  * @param {Object} config The config object
21307  */
21308  
21309 Roo.bootstrap.SecurePass = function (config) {
21310     // these go here, so the translation tool can replace them..
21311     this.errors = {
21312         PwdEmpty: "Please type a password, and then retype it to confirm.",
21313         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21314         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21315         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21316         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21317         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21318         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21319         TooWeak: "Your password is Too Weak."
21320     },
21321     this.meterLabel = "Password strength:";
21322     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21323     this.meterClass = [
21324         "roo-password-meter-tooweak", 
21325         "roo-password-meter-weak", 
21326         "roo-password-meter-medium", 
21327         "roo-password-meter-strong", 
21328         "roo-password-meter-grey"
21329     ];
21330     
21331     this.errors = {};
21332     
21333     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21334 }
21335
21336 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21337     /**
21338      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21339      * {
21340      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21341      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21342      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21343      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21344      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21345      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21346      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21347      * })
21348      */
21349     // private
21350     
21351     meterWidth: 300,
21352     errorMsg :'',    
21353     errors: false,
21354     imageRoot: '/',
21355     /**
21356      * @cfg {String/Object} Label for the strength meter (defaults to
21357      * 'Password strength:')
21358      */
21359     // private
21360     meterLabel: '',
21361     /**
21362      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21363      * ['Weak', 'Medium', 'Strong'])
21364      */
21365     // private    
21366     pwdStrengths: false,    
21367     // private
21368     strength: 0,
21369     // private
21370     _lastPwd: null,
21371     // private
21372     kCapitalLetter: 0,
21373     kSmallLetter: 1,
21374     kDigit: 2,
21375     kPunctuation: 3,
21376     
21377     insecure: false,
21378     // private
21379     initEvents: function ()
21380     {
21381         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21382
21383         if (this.el.is('input[type=password]') && Roo.isSafari) {
21384             this.el.on('keydown', this.SafariOnKeyDown, this);
21385         }
21386
21387         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21388     },
21389     // private
21390     onRender: function (ct, position)
21391     {
21392         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21393         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21394         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21395
21396         this.trigger.createChild({
21397                    cn: [
21398                     {
21399                     //id: 'PwdMeter',
21400                     tag: 'div',
21401                     cls: 'roo-password-meter-grey col-xs-12',
21402                     style: {
21403                         //width: 0,
21404                         //width: this.meterWidth + 'px'                                                
21405                         }
21406                     },
21407                     {                            
21408                          cls: 'roo-password-meter-text'                          
21409                     }
21410                 ]            
21411         });
21412
21413          
21414         if (this.hideTrigger) {
21415             this.trigger.setDisplayed(false);
21416         }
21417         this.setSize(this.width || '', this.height || '');
21418     },
21419     // private
21420     onDestroy: function ()
21421     {
21422         if (this.trigger) {
21423             this.trigger.removeAllListeners();
21424             this.trigger.remove();
21425         }
21426         if (this.wrap) {
21427             this.wrap.remove();
21428         }
21429         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21430     },
21431     // private
21432     checkStrength: function ()
21433     {
21434         var pwd = this.inputEl().getValue();
21435         if (pwd == this._lastPwd) {
21436             return;
21437         }
21438
21439         var strength;
21440         if (this.ClientSideStrongPassword(pwd)) {
21441             strength = 3;
21442         } else if (this.ClientSideMediumPassword(pwd)) {
21443             strength = 2;
21444         } else if (this.ClientSideWeakPassword(pwd)) {
21445             strength = 1;
21446         } else {
21447             strength = 0;
21448         }
21449         
21450         Roo.log('strength1: ' + strength);
21451         
21452         //var pm = this.trigger.child('div/div/div').dom;
21453         var pm = this.trigger.child('div/div');
21454         pm.removeClass(this.meterClass);
21455         pm.addClass(this.meterClass[strength]);
21456                 
21457         
21458         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21459                 
21460         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21461         
21462         this._lastPwd = pwd;
21463     },
21464     reset: function ()
21465     {
21466         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21467         
21468         this._lastPwd = '';
21469         
21470         var pm = this.trigger.child('div/div');
21471         pm.removeClass(this.meterClass);
21472         pm.addClass('roo-password-meter-grey');        
21473         
21474         
21475         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21476         
21477         pt.innerHTML = '';
21478         this.inputEl().dom.type='password';
21479     },
21480     // private
21481     validateValue: function (value)
21482     {
21483         
21484         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21485             return false;
21486         }
21487         if (value.length == 0) {
21488             if (this.allowBlank) {
21489                 this.clearInvalid();
21490                 return true;
21491             }
21492
21493             this.markInvalid(this.errors.PwdEmpty);
21494             this.errorMsg = this.errors.PwdEmpty;
21495             return false;
21496         }
21497         
21498         if(this.insecure){
21499             return true;
21500         }
21501         
21502         if ('[\x21-\x7e]*'.match(value)) {
21503             this.markInvalid(this.errors.PwdBadChar);
21504             this.errorMsg = this.errors.PwdBadChar;
21505             return false;
21506         }
21507         if (value.length < 6) {
21508             this.markInvalid(this.errors.PwdShort);
21509             this.errorMsg = this.errors.PwdShort;
21510             return false;
21511         }
21512         if (value.length > 16) {
21513             this.markInvalid(this.errors.PwdLong);
21514             this.errorMsg = this.errors.PwdLong;
21515             return false;
21516         }
21517         var strength;
21518         if (this.ClientSideStrongPassword(value)) {
21519             strength = 3;
21520         } else if (this.ClientSideMediumPassword(value)) {
21521             strength = 2;
21522         } else if (this.ClientSideWeakPassword(value)) {
21523             strength = 1;
21524         } else {
21525             strength = 0;
21526         }
21527
21528         
21529         if (strength < 2) {
21530             //this.markInvalid(this.errors.TooWeak);
21531             this.errorMsg = this.errors.TooWeak;
21532             //return false;
21533         }
21534         
21535         
21536         console.log('strength2: ' + strength);
21537         
21538         //var pm = this.trigger.child('div/div/div').dom;
21539         
21540         var pm = this.trigger.child('div/div');
21541         pm.removeClass(this.meterClass);
21542         pm.addClass(this.meterClass[strength]);
21543                 
21544         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21545                 
21546         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21547         
21548         this.errorMsg = ''; 
21549         return true;
21550     },
21551     // private
21552     CharacterSetChecks: function (type)
21553     {
21554         this.type = type;
21555         this.fResult = false;
21556     },
21557     // private
21558     isctype: function (character, type)
21559     {
21560         switch (type) {  
21561             case this.kCapitalLetter:
21562                 if (character >= 'A' && character <= 'Z') {
21563                     return true;
21564                 }
21565                 break;
21566             
21567             case this.kSmallLetter:
21568                 if (character >= 'a' && character <= 'z') {
21569                     return true;
21570                 }
21571                 break;
21572             
21573             case this.kDigit:
21574                 if (character >= '0' && character <= '9') {
21575                     return true;
21576                 }
21577                 break;
21578             
21579             case this.kPunctuation:
21580                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21581                     return true;
21582                 }
21583                 break;
21584             
21585             default:
21586                 return false;
21587         }
21588
21589     },
21590     // private
21591     IsLongEnough: function (pwd, size)
21592     {
21593         return !(pwd == null || isNaN(size) || pwd.length < size);
21594     },
21595     // private
21596     SpansEnoughCharacterSets: function (word, nb)
21597     {
21598         if (!this.IsLongEnough(word, nb))
21599         {
21600             return false;
21601         }
21602
21603         var characterSetChecks = new Array(
21604             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21605             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21606         );
21607         
21608         for (var index = 0; index < word.length; ++index) {
21609             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21610                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21611                     characterSetChecks[nCharSet].fResult = true;
21612                     break;
21613                 }
21614             }
21615         }
21616
21617         var nCharSets = 0;
21618         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21619             if (characterSetChecks[nCharSet].fResult) {
21620                 ++nCharSets;
21621             }
21622         }
21623
21624         if (nCharSets < nb) {
21625             return false;
21626         }
21627         return true;
21628     },
21629     // private
21630     ClientSideStrongPassword: function (pwd)
21631     {
21632         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21633     },
21634     // private
21635     ClientSideMediumPassword: function (pwd)
21636     {
21637         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21638     },
21639     // private
21640     ClientSideWeakPassword: function (pwd)
21641     {
21642         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21643     }
21644           
21645 })//<script type="text/javascript">
21646
21647 /*
21648  * Based  Ext JS Library 1.1.1
21649  * Copyright(c) 2006-2007, Ext JS, LLC.
21650  * LGPL
21651  *
21652  */
21653  
21654 /**
21655  * @class Roo.HtmlEditorCore
21656  * @extends Roo.Component
21657  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21658  *
21659  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21660  */
21661
21662 Roo.HtmlEditorCore = function(config){
21663     
21664     
21665     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21666     
21667     
21668     this.addEvents({
21669         /**
21670          * @event initialize
21671          * Fires when the editor is fully initialized (including the iframe)
21672          * @param {Roo.HtmlEditorCore} this
21673          */
21674         initialize: true,
21675         /**
21676          * @event activate
21677          * Fires when the editor is first receives the focus. Any insertion must wait
21678          * until after this event.
21679          * @param {Roo.HtmlEditorCore} this
21680          */
21681         activate: true,
21682          /**
21683          * @event beforesync
21684          * Fires before the textarea is updated with content from the editor iframe. Return false
21685          * to cancel the sync.
21686          * @param {Roo.HtmlEditorCore} this
21687          * @param {String} html
21688          */
21689         beforesync: true,
21690          /**
21691          * @event beforepush
21692          * Fires before the iframe editor is updated with content from the textarea. Return false
21693          * to cancel the push.
21694          * @param {Roo.HtmlEditorCore} this
21695          * @param {String} html
21696          */
21697         beforepush: true,
21698          /**
21699          * @event sync
21700          * Fires when the textarea is updated with content from the editor iframe.
21701          * @param {Roo.HtmlEditorCore} this
21702          * @param {String} html
21703          */
21704         sync: true,
21705          /**
21706          * @event push
21707          * Fires when the iframe editor is updated with content from the textarea.
21708          * @param {Roo.HtmlEditorCore} this
21709          * @param {String} html
21710          */
21711         push: true,
21712         
21713         /**
21714          * @event editorevent
21715          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21716          * @param {Roo.HtmlEditorCore} this
21717          */
21718         editorevent: true
21719         
21720     });
21721     
21722     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21723     
21724     // defaults : white / black...
21725     this.applyBlacklists();
21726     
21727     
21728     
21729 };
21730
21731
21732 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21733
21734
21735      /**
21736      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21737      */
21738     
21739     owner : false,
21740     
21741      /**
21742      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21743      *                        Roo.resizable.
21744      */
21745     resizable : false,
21746      /**
21747      * @cfg {Number} height (in pixels)
21748      */   
21749     height: 300,
21750    /**
21751      * @cfg {Number} width (in pixels)
21752      */   
21753     width: 500,
21754     
21755     /**
21756      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21757      * 
21758      */
21759     stylesheets: false,
21760     
21761     // id of frame..
21762     frameId: false,
21763     
21764     // private properties
21765     validationEvent : false,
21766     deferHeight: true,
21767     initialized : false,
21768     activated : false,
21769     sourceEditMode : false,
21770     onFocus : Roo.emptyFn,
21771     iframePad:3,
21772     hideMode:'offsets',
21773     
21774     clearUp: true,
21775     
21776     // blacklist + whitelisted elements..
21777     black: false,
21778     white: false,
21779      
21780     bodyCls : '',
21781
21782     /**
21783      * Protected method that will not generally be called directly. It
21784      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21785      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21786      */
21787     getDocMarkup : function(){
21788         // body styles..
21789         var st = '';
21790         
21791         // inherit styels from page...?? 
21792         if (this.stylesheets === false) {
21793             
21794             Roo.get(document.head).select('style').each(function(node) {
21795                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21796             });
21797             
21798             Roo.get(document.head).select('link').each(function(node) { 
21799                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21800             });
21801             
21802         } else if (!this.stylesheets.length) {
21803                 // simple..
21804                 st = '<style type="text/css">' +
21805                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21806                    '</style>';
21807         } else { 
21808             st = '<style type="text/css">' +
21809                     this.stylesheets +
21810                 '</style>';
21811         }
21812         
21813         st +=  '<style type="text/css">' +
21814             'IMG { cursor: pointer } ' +
21815         '</style>';
21816
21817         var cls = 'roo-htmleditor-body';
21818         
21819         if(this.bodyCls.length){
21820             cls += ' ' + this.bodyCls;
21821         }
21822         
21823         return '<html><head>' + st  +
21824             //<style type="text/css">' +
21825             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21826             //'</style>' +
21827             ' </head><body class="' +  cls + '"></body></html>';
21828     },
21829
21830     // private
21831     onRender : function(ct, position)
21832     {
21833         var _t = this;
21834         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21835         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21836         
21837         
21838         this.el.dom.style.border = '0 none';
21839         this.el.dom.setAttribute('tabIndex', -1);
21840         this.el.addClass('x-hidden hide');
21841         
21842         
21843         
21844         if(Roo.isIE){ // fix IE 1px bogus margin
21845             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21846         }
21847        
21848         
21849         this.frameId = Roo.id();
21850         
21851          
21852         
21853         var iframe = this.owner.wrap.createChild({
21854             tag: 'iframe',
21855             cls: 'form-control', // bootstrap..
21856             id: this.frameId,
21857             name: this.frameId,
21858             frameBorder : 'no',
21859             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21860         }, this.el
21861         );
21862         
21863         
21864         this.iframe = iframe.dom;
21865
21866          this.assignDocWin();
21867         
21868         this.doc.designMode = 'on';
21869        
21870         this.doc.open();
21871         this.doc.write(this.getDocMarkup());
21872         this.doc.close();
21873
21874         
21875         var task = { // must defer to wait for browser to be ready
21876             run : function(){
21877                 //console.log("run task?" + this.doc.readyState);
21878                 this.assignDocWin();
21879                 if(this.doc.body || this.doc.readyState == 'complete'){
21880                     try {
21881                         this.doc.designMode="on";
21882                     } catch (e) {
21883                         return;
21884                     }
21885                     Roo.TaskMgr.stop(task);
21886                     this.initEditor.defer(10, this);
21887                 }
21888             },
21889             interval : 10,
21890             duration: 10000,
21891             scope: this
21892         };
21893         Roo.TaskMgr.start(task);
21894
21895     },
21896
21897     // private
21898     onResize : function(w, h)
21899     {
21900          Roo.log('resize: ' +w + ',' + h );
21901         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21902         if(!this.iframe){
21903             return;
21904         }
21905         if(typeof w == 'number'){
21906             
21907             this.iframe.style.width = w + 'px';
21908         }
21909         if(typeof h == 'number'){
21910             
21911             this.iframe.style.height = h + 'px';
21912             if(this.doc){
21913                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21914             }
21915         }
21916         
21917     },
21918
21919     /**
21920      * Toggles the editor between standard and source edit mode.
21921      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21922      */
21923     toggleSourceEdit : function(sourceEditMode){
21924         
21925         this.sourceEditMode = sourceEditMode === true;
21926         
21927         if(this.sourceEditMode){
21928  
21929             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21930             
21931         }else{
21932             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21933             //this.iframe.className = '';
21934             this.deferFocus();
21935         }
21936         //this.setSize(this.owner.wrap.getSize());
21937         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21938     },
21939
21940     
21941   
21942
21943     /**
21944      * Protected method that will not generally be called directly. If you need/want
21945      * custom HTML cleanup, this is the method you should override.
21946      * @param {String} html The HTML to be cleaned
21947      * return {String} The cleaned HTML
21948      */
21949     cleanHtml : function(html){
21950         html = String(html);
21951         if(html.length > 5){
21952             if(Roo.isSafari){ // strip safari nonsense
21953                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21954             }
21955         }
21956         if(html == '&nbsp;'){
21957             html = '';
21958         }
21959         return html;
21960     },
21961
21962     /**
21963      * HTML Editor -> Textarea
21964      * Protected method that will not generally be called directly. Syncs the contents
21965      * of the editor iframe with the textarea.
21966      */
21967     syncValue : function(){
21968         if(this.initialized){
21969             var bd = (this.doc.body || this.doc.documentElement);
21970             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21971             var html = bd.innerHTML;
21972             if(Roo.isSafari){
21973                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21974                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21975                 if(m && m[1]){
21976                     html = '<div style="'+m[0]+'">' + html + '</div>';
21977                 }
21978             }
21979             html = this.cleanHtml(html);
21980             // fix up the special chars.. normaly like back quotes in word...
21981             // however we do not want to do this with chinese..
21982             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21983                 var cc = b.charCodeAt();
21984                 if (
21985                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21986                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21987                     (cc >= 0xf900 && cc < 0xfb00 )
21988                 ) {
21989                         return b;
21990                 }
21991                 return "&#"+cc+";" 
21992             });
21993             if(this.owner.fireEvent('beforesync', this, html) !== false){
21994                 this.el.dom.value = html;
21995                 this.owner.fireEvent('sync', this, html);
21996             }
21997         }
21998     },
21999
22000     /**
22001      * Protected method that will not generally be called directly. Pushes the value of the textarea
22002      * into the iframe editor.
22003      */
22004     pushValue : function(){
22005         if(this.initialized){
22006             var v = this.el.dom.value.trim();
22007             
22008 //            if(v.length < 1){
22009 //                v = '&#160;';
22010 //            }
22011             
22012             if(this.owner.fireEvent('beforepush', this, v) !== false){
22013                 var d = (this.doc.body || this.doc.documentElement);
22014                 d.innerHTML = v;
22015                 this.cleanUpPaste();
22016                 this.el.dom.value = d.innerHTML;
22017                 this.owner.fireEvent('push', this, v);
22018             }
22019         }
22020     },
22021
22022     // private
22023     deferFocus : function(){
22024         this.focus.defer(10, this);
22025     },
22026
22027     // doc'ed in Field
22028     focus : function(){
22029         if(this.win && !this.sourceEditMode){
22030             this.win.focus();
22031         }else{
22032             this.el.focus();
22033         }
22034     },
22035     
22036     assignDocWin: function()
22037     {
22038         var iframe = this.iframe;
22039         
22040          if(Roo.isIE){
22041             this.doc = iframe.contentWindow.document;
22042             this.win = iframe.contentWindow;
22043         } else {
22044 //            if (!Roo.get(this.frameId)) {
22045 //                return;
22046 //            }
22047 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22048 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22049             
22050             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22051                 return;
22052             }
22053             
22054             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22055             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22056         }
22057     },
22058     
22059     // private
22060     initEditor : function(){
22061         //console.log("INIT EDITOR");
22062         this.assignDocWin();
22063         
22064         
22065         
22066         this.doc.designMode="on";
22067         this.doc.open();
22068         this.doc.write(this.getDocMarkup());
22069         this.doc.close();
22070         
22071         var dbody = (this.doc.body || this.doc.documentElement);
22072         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22073         // this copies styles from the containing element into thsi one..
22074         // not sure why we need all of this..
22075         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22076         
22077         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22078         //ss['background-attachment'] = 'fixed'; // w3c
22079         dbody.bgProperties = 'fixed'; // ie
22080         //Roo.DomHelper.applyStyles(dbody, ss);
22081         Roo.EventManager.on(this.doc, {
22082             //'mousedown': this.onEditorEvent,
22083             'mouseup': this.onEditorEvent,
22084             'dblclick': this.onEditorEvent,
22085             'click': this.onEditorEvent,
22086             'keyup': this.onEditorEvent,
22087             buffer:100,
22088             scope: this
22089         });
22090         if(Roo.isGecko){
22091             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22092         }
22093         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22094             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22095         }
22096         this.initialized = true;
22097
22098         this.owner.fireEvent('initialize', this);
22099         this.pushValue();
22100     },
22101
22102     // private
22103     onDestroy : function(){
22104         
22105         
22106         
22107         if(this.rendered){
22108             
22109             //for (var i =0; i < this.toolbars.length;i++) {
22110             //    // fixme - ask toolbars for heights?
22111             //    this.toolbars[i].onDestroy();
22112            // }
22113             
22114             //this.wrap.dom.innerHTML = '';
22115             //this.wrap.remove();
22116         }
22117     },
22118
22119     // private
22120     onFirstFocus : function(){
22121         
22122         this.assignDocWin();
22123         
22124         
22125         this.activated = true;
22126          
22127     
22128         if(Roo.isGecko){ // prevent silly gecko errors
22129             this.win.focus();
22130             var s = this.win.getSelection();
22131             if(!s.focusNode || s.focusNode.nodeType != 3){
22132                 var r = s.getRangeAt(0);
22133                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22134                 r.collapse(true);
22135                 this.deferFocus();
22136             }
22137             try{
22138                 this.execCmd('useCSS', true);
22139                 this.execCmd('styleWithCSS', false);
22140             }catch(e){}
22141         }
22142         this.owner.fireEvent('activate', this);
22143     },
22144
22145     // private
22146     adjustFont: function(btn){
22147         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22148         //if(Roo.isSafari){ // safari
22149         //    adjust *= 2;
22150        // }
22151         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22152         if(Roo.isSafari){ // safari
22153             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22154             v =  (v < 10) ? 10 : v;
22155             v =  (v > 48) ? 48 : v;
22156             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22157             
22158         }
22159         
22160         
22161         v = Math.max(1, v+adjust);
22162         
22163         this.execCmd('FontSize', v  );
22164     },
22165
22166     onEditorEvent : function(e)
22167     {
22168         this.owner.fireEvent('editorevent', this, e);
22169       //  this.updateToolbar();
22170         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22171     },
22172
22173     insertTag : function(tg)
22174     {
22175         // could be a bit smarter... -> wrap the current selected tRoo..
22176         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22177             
22178             range = this.createRange(this.getSelection());
22179             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22180             wrappingNode.appendChild(range.extractContents());
22181             range.insertNode(wrappingNode);
22182
22183             return;
22184             
22185             
22186             
22187         }
22188         this.execCmd("formatblock",   tg);
22189         
22190     },
22191     
22192     insertText : function(txt)
22193     {
22194         
22195         
22196         var range = this.createRange();
22197         range.deleteContents();
22198                //alert(Sender.getAttribute('label'));
22199                
22200         range.insertNode(this.doc.createTextNode(txt));
22201     } ,
22202     
22203      
22204
22205     /**
22206      * Executes a Midas editor command on the editor document and performs necessary focus and
22207      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22208      * @param {String} cmd The Midas command
22209      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22210      */
22211     relayCmd : function(cmd, value){
22212         this.win.focus();
22213         this.execCmd(cmd, value);
22214         this.owner.fireEvent('editorevent', this);
22215         //this.updateToolbar();
22216         this.owner.deferFocus();
22217     },
22218
22219     /**
22220      * Executes a Midas editor command directly on the editor document.
22221      * For visual commands, you should use {@link #relayCmd} instead.
22222      * <b>This should only be called after the editor is initialized.</b>
22223      * @param {String} cmd The Midas command
22224      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22225      */
22226     execCmd : function(cmd, value){
22227         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22228         this.syncValue();
22229     },
22230  
22231  
22232    
22233     /**
22234      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22235      * to insert tRoo.
22236      * @param {String} text | dom node.. 
22237      */
22238     insertAtCursor : function(text)
22239     {
22240         
22241         if(!this.activated){
22242             return;
22243         }
22244         /*
22245         if(Roo.isIE){
22246             this.win.focus();
22247             var r = this.doc.selection.createRange();
22248             if(r){
22249                 r.collapse(true);
22250                 r.pasteHTML(text);
22251                 this.syncValue();
22252                 this.deferFocus();
22253             
22254             }
22255             return;
22256         }
22257         */
22258         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22259             this.win.focus();
22260             
22261             
22262             // from jquery ui (MIT licenced)
22263             var range, node;
22264             var win = this.win;
22265             
22266             if (win.getSelection && win.getSelection().getRangeAt) {
22267                 range = win.getSelection().getRangeAt(0);
22268                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22269                 range.insertNode(node);
22270             } else if (win.document.selection && win.document.selection.createRange) {
22271                 // no firefox support
22272                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22273                 win.document.selection.createRange().pasteHTML(txt);
22274             } else {
22275                 // no firefox support
22276                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22277                 this.execCmd('InsertHTML', txt);
22278             } 
22279             
22280             this.syncValue();
22281             
22282             this.deferFocus();
22283         }
22284     },
22285  // private
22286     mozKeyPress : function(e){
22287         if(e.ctrlKey){
22288             var c = e.getCharCode(), cmd;
22289           
22290             if(c > 0){
22291                 c = String.fromCharCode(c).toLowerCase();
22292                 switch(c){
22293                     case 'b':
22294                         cmd = 'bold';
22295                         break;
22296                     case 'i':
22297                         cmd = 'italic';
22298                         break;
22299                     
22300                     case 'u':
22301                         cmd = 'underline';
22302                         break;
22303                     
22304                     case 'v':
22305                         this.cleanUpPaste.defer(100, this);
22306                         return;
22307                         
22308                 }
22309                 if(cmd){
22310                     this.win.focus();
22311                     this.execCmd(cmd);
22312                     this.deferFocus();
22313                     e.preventDefault();
22314                 }
22315                 
22316             }
22317         }
22318     },
22319
22320     // private
22321     fixKeys : function(){ // load time branching for fastest keydown performance
22322         if(Roo.isIE){
22323             return function(e){
22324                 var k = e.getKey(), r;
22325                 if(k == e.TAB){
22326                     e.stopEvent();
22327                     r = this.doc.selection.createRange();
22328                     if(r){
22329                         r.collapse(true);
22330                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22331                         this.deferFocus();
22332                     }
22333                     return;
22334                 }
22335                 
22336                 if(k == e.ENTER){
22337                     r = this.doc.selection.createRange();
22338                     if(r){
22339                         var target = r.parentElement();
22340                         if(!target || target.tagName.toLowerCase() != 'li'){
22341                             e.stopEvent();
22342                             r.pasteHTML('<br />');
22343                             r.collapse(false);
22344                             r.select();
22345                         }
22346                     }
22347                 }
22348                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22349                     this.cleanUpPaste.defer(100, this);
22350                     return;
22351                 }
22352                 
22353                 
22354             };
22355         }else if(Roo.isOpera){
22356             return function(e){
22357                 var k = e.getKey();
22358                 if(k == e.TAB){
22359                     e.stopEvent();
22360                     this.win.focus();
22361                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22362                     this.deferFocus();
22363                 }
22364                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22365                     this.cleanUpPaste.defer(100, this);
22366                     return;
22367                 }
22368                 
22369             };
22370         }else if(Roo.isSafari){
22371             return function(e){
22372                 var k = e.getKey();
22373                 
22374                 if(k == e.TAB){
22375                     e.stopEvent();
22376                     this.execCmd('InsertText','\t');
22377                     this.deferFocus();
22378                     return;
22379                 }
22380                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22381                     this.cleanUpPaste.defer(100, this);
22382                     return;
22383                 }
22384                 
22385              };
22386         }
22387     }(),
22388     
22389     getAllAncestors: function()
22390     {
22391         var p = this.getSelectedNode();
22392         var a = [];
22393         if (!p) {
22394             a.push(p); // push blank onto stack..
22395             p = this.getParentElement();
22396         }
22397         
22398         
22399         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22400             a.push(p);
22401             p = p.parentNode;
22402         }
22403         a.push(this.doc.body);
22404         return a;
22405     },
22406     lastSel : false,
22407     lastSelNode : false,
22408     
22409     
22410     getSelection : function() 
22411     {
22412         this.assignDocWin();
22413         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22414     },
22415     
22416     getSelectedNode: function() 
22417     {
22418         // this may only work on Gecko!!!
22419         
22420         // should we cache this!!!!
22421         
22422         
22423         
22424          
22425         var range = this.createRange(this.getSelection()).cloneRange();
22426         
22427         if (Roo.isIE) {
22428             var parent = range.parentElement();
22429             while (true) {
22430                 var testRange = range.duplicate();
22431                 testRange.moveToElementText(parent);
22432                 if (testRange.inRange(range)) {
22433                     break;
22434                 }
22435                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22436                     break;
22437                 }
22438                 parent = parent.parentElement;
22439             }
22440             return parent;
22441         }
22442         
22443         // is ancestor a text element.
22444         var ac =  range.commonAncestorContainer;
22445         if (ac.nodeType == 3) {
22446             ac = ac.parentNode;
22447         }
22448         
22449         var ar = ac.childNodes;
22450          
22451         var nodes = [];
22452         var other_nodes = [];
22453         var has_other_nodes = false;
22454         for (var i=0;i<ar.length;i++) {
22455             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22456                 continue;
22457             }
22458             // fullly contained node.
22459             
22460             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22461                 nodes.push(ar[i]);
22462                 continue;
22463             }
22464             
22465             // probably selected..
22466             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22467                 other_nodes.push(ar[i]);
22468                 continue;
22469             }
22470             // outer..
22471             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22472                 continue;
22473             }
22474             
22475             
22476             has_other_nodes = true;
22477         }
22478         if (!nodes.length && other_nodes.length) {
22479             nodes= other_nodes;
22480         }
22481         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22482             return false;
22483         }
22484         
22485         return nodes[0];
22486     },
22487     createRange: function(sel)
22488     {
22489         // this has strange effects when using with 
22490         // top toolbar - not sure if it's a great idea.
22491         //this.editor.contentWindow.focus();
22492         if (typeof sel != "undefined") {
22493             try {
22494                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22495             } catch(e) {
22496                 return this.doc.createRange();
22497             }
22498         } else {
22499             return this.doc.createRange();
22500         }
22501     },
22502     getParentElement: function()
22503     {
22504         
22505         this.assignDocWin();
22506         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22507         
22508         var range = this.createRange(sel);
22509          
22510         try {
22511             var p = range.commonAncestorContainer;
22512             while (p.nodeType == 3) { // text node
22513                 p = p.parentNode;
22514             }
22515             return p;
22516         } catch (e) {
22517             return null;
22518         }
22519     
22520     },
22521     /***
22522      *
22523      * Range intersection.. the hard stuff...
22524      *  '-1' = before
22525      *  '0' = hits..
22526      *  '1' = after.
22527      *         [ -- selected range --- ]
22528      *   [fail]                        [fail]
22529      *
22530      *    basically..
22531      *      if end is before start or  hits it. fail.
22532      *      if start is after end or hits it fail.
22533      *
22534      *   if either hits (but other is outside. - then it's not 
22535      *   
22536      *    
22537      **/
22538     
22539     
22540     // @see http://www.thismuchiknow.co.uk/?p=64.
22541     rangeIntersectsNode : function(range, node)
22542     {
22543         var nodeRange = node.ownerDocument.createRange();
22544         try {
22545             nodeRange.selectNode(node);
22546         } catch (e) {
22547             nodeRange.selectNodeContents(node);
22548         }
22549     
22550         var rangeStartRange = range.cloneRange();
22551         rangeStartRange.collapse(true);
22552     
22553         var rangeEndRange = range.cloneRange();
22554         rangeEndRange.collapse(false);
22555     
22556         var nodeStartRange = nodeRange.cloneRange();
22557         nodeStartRange.collapse(true);
22558     
22559         var nodeEndRange = nodeRange.cloneRange();
22560         nodeEndRange.collapse(false);
22561     
22562         return rangeStartRange.compareBoundaryPoints(
22563                  Range.START_TO_START, nodeEndRange) == -1 &&
22564                rangeEndRange.compareBoundaryPoints(
22565                  Range.START_TO_START, nodeStartRange) == 1;
22566         
22567          
22568     },
22569     rangeCompareNode : function(range, node)
22570     {
22571         var nodeRange = node.ownerDocument.createRange();
22572         try {
22573             nodeRange.selectNode(node);
22574         } catch (e) {
22575             nodeRange.selectNodeContents(node);
22576         }
22577         
22578         
22579         range.collapse(true);
22580     
22581         nodeRange.collapse(true);
22582      
22583         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22584         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22585          
22586         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22587         
22588         var nodeIsBefore   =  ss == 1;
22589         var nodeIsAfter    = ee == -1;
22590         
22591         if (nodeIsBefore && nodeIsAfter) {
22592             return 0; // outer
22593         }
22594         if (!nodeIsBefore && nodeIsAfter) {
22595             return 1; //right trailed.
22596         }
22597         
22598         if (nodeIsBefore && !nodeIsAfter) {
22599             return 2;  // left trailed.
22600         }
22601         // fully contined.
22602         return 3;
22603     },
22604
22605     // private? - in a new class?
22606     cleanUpPaste :  function()
22607     {
22608         // cleans up the whole document..
22609         Roo.log('cleanuppaste');
22610         
22611         this.cleanUpChildren(this.doc.body);
22612         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22613         if (clean != this.doc.body.innerHTML) {
22614             this.doc.body.innerHTML = clean;
22615         }
22616         
22617     },
22618     
22619     cleanWordChars : function(input) {// change the chars to hex code
22620         var he = Roo.HtmlEditorCore;
22621         
22622         var output = input;
22623         Roo.each(he.swapCodes, function(sw) { 
22624             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22625             
22626             output = output.replace(swapper, sw[1]);
22627         });
22628         
22629         return output;
22630     },
22631     
22632     
22633     cleanUpChildren : function (n)
22634     {
22635         if (!n.childNodes.length) {
22636             return;
22637         }
22638         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22639            this.cleanUpChild(n.childNodes[i]);
22640         }
22641     },
22642     
22643     
22644         
22645     
22646     cleanUpChild : function (node)
22647     {
22648         var ed = this;
22649         //console.log(node);
22650         if (node.nodeName == "#text") {
22651             // clean up silly Windows -- stuff?
22652             return; 
22653         }
22654         if (node.nodeName == "#comment") {
22655             node.parentNode.removeChild(node);
22656             // clean up silly Windows -- stuff?
22657             return; 
22658         }
22659         var lcname = node.tagName.toLowerCase();
22660         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22661         // whitelist of tags..
22662         
22663         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22664             // remove node.
22665             node.parentNode.removeChild(node);
22666             return;
22667             
22668         }
22669         
22670         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22671         
22672         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22673         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22674         
22675         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22676         //    remove_keep_children = true;
22677         //}
22678         
22679         if (remove_keep_children) {
22680             this.cleanUpChildren(node);
22681             // inserts everything just before this node...
22682             while (node.childNodes.length) {
22683                 var cn = node.childNodes[0];
22684                 node.removeChild(cn);
22685                 node.parentNode.insertBefore(cn, node);
22686             }
22687             node.parentNode.removeChild(node);
22688             return;
22689         }
22690         
22691         if (!node.attributes || !node.attributes.length) {
22692             this.cleanUpChildren(node);
22693             return;
22694         }
22695         
22696         function cleanAttr(n,v)
22697         {
22698             
22699             if (v.match(/^\./) || v.match(/^\//)) {
22700                 return;
22701             }
22702             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22703                 return;
22704             }
22705             if (v.match(/^#/)) {
22706                 return;
22707             }
22708 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22709             node.removeAttribute(n);
22710             
22711         }
22712         
22713         var cwhite = this.cwhite;
22714         var cblack = this.cblack;
22715             
22716         function cleanStyle(n,v)
22717         {
22718             if (v.match(/expression/)) { //XSS?? should we even bother..
22719                 node.removeAttribute(n);
22720                 return;
22721             }
22722             
22723             var parts = v.split(/;/);
22724             var clean = [];
22725             
22726             Roo.each(parts, function(p) {
22727                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22728                 if (!p.length) {
22729                     return true;
22730                 }
22731                 var l = p.split(':').shift().replace(/\s+/g,'');
22732                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22733                 
22734                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22735 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22736                     //node.removeAttribute(n);
22737                     return true;
22738                 }
22739                 //Roo.log()
22740                 // only allow 'c whitelisted system attributes'
22741                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22742 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22743                     //node.removeAttribute(n);
22744                     return true;
22745                 }
22746                 
22747                 
22748                  
22749                 
22750                 clean.push(p);
22751                 return true;
22752             });
22753             if (clean.length) { 
22754                 node.setAttribute(n, clean.join(';'));
22755             } else {
22756                 node.removeAttribute(n);
22757             }
22758             
22759         }
22760         
22761         
22762         for (var i = node.attributes.length-1; i > -1 ; i--) {
22763             var a = node.attributes[i];
22764             //console.log(a);
22765             
22766             if (a.name.toLowerCase().substr(0,2)=='on')  {
22767                 node.removeAttribute(a.name);
22768                 continue;
22769             }
22770             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22771                 node.removeAttribute(a.name);
22772                 continue;
22773             }
22774             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22775                 cleanAttr(a.name,a.value); // fixme..
22776                 continue;
22777             }
22778             if (a.name == 'style') {
22779                 cleanStyle(a.name,a.value);
22780                 continue;
22781             }
22782             /// clean up MS crap..
22783             // tecnically this should be a list of valid class'es..
22784             
22785             
22786             if (a.name == 'class') {
22787                 if (a.value.match(/^Mso/)) {
22788                     node.className = '';
22789                 }
22790                 
22791                 if (a.value.match(/^body$/)) {
22792                     node.className = '';
22793                 }
22794                 continue;
22795             }
22796             
22797             // style cleanup!?
22798             // class cleanup?
22799             
22800         }
22801         
22802         
22803         this.cleanUpChildren(node);
22804         
22805         
22806     },
22807     
22808     /**
22809      * Clean up MS wordisms...
22810      */
22811     cleanWord : function(node)
22812     {
22813         
22814         
22815         if (!node) {
22816             this.cleanWord(this.doc.body);
22817             return;
22818         }
22819         if (node.nodeName == "#text") {
22820             // clean up silly Windows -- stuff?
22821             return; 
22822         }
22823         if (node.nodeName == "#comment") {
22824             node.parentNode.removeChild(node);
22825             // clean up silly Windows -- stuff?
22826             return; 
22827         }
22828         
22829         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22830             node.parentNode.removeChild(node);
22831             return;
22832         }
22833         
22834         // remove - but keep children..
22835         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22836             while (node.childNodes.length) {
22837                 var cn = node.childNodes[0];
22838                 node.removeChild(cn);
22839                 node.parentNode.insertBefore(cn, node);
22840             }
22841             node.parentNode.removeChild(node);
22842             this.iterateChildren(node, this.cleanWord);
22843             return;
22844         }
22845         // clean styles
22846         if (node.className.length) {
22847             
22848             var cn = node.className.split(/\W+/);
22849             var cna = [];
22850             Roo.each(cn, function(cls) {
22851                 if (cls.match(/Mso[a-zA-Z]+/)) {
22852                     return;
22853                 }
22854                 cna.push(cls);
22855             });
22856             node.className = cna.length ? cna.join(' ') : '';
22857             if (!cna.length) {
22858                 node.removeAttribute("class");
22859             }
22860         }
22861         
22862         if (node.hasAttribute("lang")) {
22863             node.removeAttribute("lang");
22864         }
22865         
22866         if (node.hasAttribute("style")) {
22867             
22868             var styles = node.getAttribute("style").split(";");
22869             var nstyle = [];
22870             Roo.each(styles, function(s) {
22871                 if (!s.match(/:/)) {
22872                     return;
22873                 }
22874                 var kv = s.split(":");
22875                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22876                     return;
22877                 }
22878                 // what ever is left... we allow.
22879                 nstyle.push(s);
22880             });
22881             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22882             if (!nstyle.length) {
22883                 node.removeAttribute('style');
22884             }
22885         }
22886         this.iterateChildren(node, this.cleanWord);
22887         
22888         
22889         
22890     },
22891     /**
22892      * iterateChildren of a Node, calling fn each time, using this as the scole..
22893      * @param {DomNode} node node to iterate children of.
22894      * @param {Function} fn method of this class to call on each item.
22895      */
22896     iterateChildren : function(node, fn)
22897     {
22898         if (!node.childNodes.length) {
22899                 return;
22900         }
22901         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22902            fn.call(this, node.childNodes[i])
22903         }
22904     },
22905     
22906     
22907     /**
22908      * cleanTableWidths.
22909      *
22910      * Quite often pasting from word etc.. results in tables with column and widths.
22911      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22912      *
22913      */
22914     cleanTableWidths : function(node)
22915     {
22916          
22917          
22918         if (!node) {
22919             this.cleanTableWidths(this.doc.body);
22920             return;
22921         }
22922         
22923         // ignore list...
22924         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22925             return; 
22926         }
22927         Roo.log(node.tagName);
22928         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22929             this.iterateChildren(node, this.cleanTableWidths);
22930             return;
22931         }
22932         if (node.hasAttribute('width')) {
22933             node.removeAttribute('width');
22934         }
22935         
22936          
22937         if (node.hasAttribute("style")) {
22938             // pretty basic...
22939             
22940             var styles = node.getAttribute("style").split(";");
22941             var nstyle = [];
22942             Roo.each(styles, function(s) {
22943                 if (!s.match(/:/)) {
22944                     return;
22945                 }
22946                 var kv = s.split(":");
22947                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22948                     return;
22949                 }
22950                 // what ever is left... we allow.
22951                 nstyle.push(s);
22952             });
22953             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22954             if (!nstyle.length) {
22955                 node.removeAttribute('style');
22956             }
22957         }
22958         
22959         this.iterateChildren(node, this.cleanTableWidths);
22960         
22961         
22962     },
22963     
22964     
22965     
22966     
22967     domToHTML : function(currentElement, depth, nopadtext) {
22968         
22969         depth = depth || 0;
22970         nopadtext = nopadtext || false;
22971     
22972         if (!currentElement) {
22973             return this.domToHTML(this.doc.body);
22974         }
22975         
22976         //Roo.log(currentElement);
22977         var j;
22978         var allText = false;
22979         var nodeName = currentElement.nodeName;
22980         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22981         
22982         if  (nodeName == '#text') {
22983             
22984             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22985         }
22986         
22987         
22988         var ret = '';
22989         if (nodeName != 'BODY') {
22990              
22991             var i = 0;
22992             // Prints the node tagName, such as <A>, <IMG>, etc
22993             if (tagName) {
22994                 var attr = [];
22995                 for(i = 0; i < currentElement.attributes.length;i++) {
22996                     // quoting?
22997                     var aname = currentElement.attributes.item(i).name;
22998                     if (!currentElement.attributes.item(i).value.length) {
22999                         continue;
23000                     }
23001                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23002                 }
23003                 
23004                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23005             } 
23006             else {
23007                 
23008                 // eack
23009             }
23010         } else {
23011             tagName = false;
23012         }
23013         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23014             return ret;
23015         }
23016         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23017             nopadtext = true;
23018         }
23019         
23020         
23021         // Traverse the tree
23022         i = 0;
23023         var currentElementChild = currentElement.childNodes.item(i);
23024         var allText = true;
23025         var innerHTML  = '';
23026         lastnode = '';
23027         while (currentElementChild) {
23028             // Formatting code (indent the tree so it looks nice on the screen)
23029             var nopad = nopadtext;
23030             if (lastnode == 'SPAN') {
23031                 nopad  = true;
23032             }
23033             // text
23034             if  (currentElementChild.nodeName == '#text') {
23035                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23036                 toadd = nopadtext ? toadd : toadd.trim();
23037                 if (!nopad && toadd.length > 80) {
23038                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23039                 }
23040                 innerHTML  += toadd;
23041                 
23042                 i++;
23043                 currentElementChild = currentElement.childNodes.item(i);
23044                 lastNode = '';
23045                 continue;
23046             }
23047             allText = false;
23048             
23049             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23050                 
23051             // Recursively traverse the tree structure of the child node
23052             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23053             lastnode = currentElementChild.nodeName;
23054             i++;
23055             currentElementChild=currentElement.childNodes.item(i);
23056         }
23057         
23058         ret += innerHTML;
23059         
23060         if (!allText) {
23061                 // The remaining code is mostly for formatting the tree
23062             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23063         }
23064         
23065         
23066         if (tagName) {
23067             ret+= "</"+tagName+">";
23068         }
23069         return ret;
23070         
23071     },
23072         
23073     applyBlacklists : function()
23074     {
23075         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23076         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23077         
23078         this.white = [];
23079         this.black = [];
23080         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23081             if (b.indexOf(tag) > -1) {
23082                 return;
23083             }
23084             this.white.push(tag);
23085             
23086         }, this);
23087         
23088         Roo.each(w, function(tag) {
23089             if (b.indexOf(tag) > -1) {
23090                 return;
23091             }
23092             if (this.white.indexOf(tag) > -1) {
23093                 return;
23094             }
23095             this.white.push(tag);
23096             
23097         }, this);
23098         
23099         
23100         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23101             if (w.indexOf(tag) > -1) {
23102                 return;
23103             }
23104             this.black.push(tag);
23105             
23106         }, this);
23107         
23108         Roo.each(b, function(tag) {
23109             if (w.indexOf(tag) > -1) {
23110                 return;
23111             }
23112             if (this.black.indexOf(tag) > -1) {
23113                 return;
23114             }
23115             this.black.push(tag);
23116             
23117         }, this);
23118         
23119         
23120         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23121         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23122         
23123         this.cwhite = [];
23124         this.cblack = [];
23125         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23126             if (b.indexOf(tag) > -1) {
23127                 return;
23128             }
23129             this.cwhite.push(tag);
23130             
23131         }, this);
23132         
23133         Roo.each(w, function(tag) {
23134             if (b.indexOf(tag) > -1) {
23135                 return;
23136             }
23137             if (this.cwhite.indexOf(tag) > -1) {
23138                 return;
23139             }
23140             this.cwhite.push(tag);
23141             
23142         }, this);
23143         
23144         
23145         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23146             if (w.indexOf(tag) > -1) {
23147                 return;
23148             }
23149             this.cblack.push(tag);
23150             
23151         }, this);
23152         
23153         Roo.each(b, function(tag) {
23154             if (w.indexOf(tag) > -1) {
23155                 return;
23156             }
23157             if (this.cblack.indexOf(tag) > -1) {
23158                 return;
23159             }
23160             this.cblack.push(tag);
23161             
23162         }, this);
23163     },
23164     
23165     setStylesheets : function(stylesheets)
23166     {
23167         if(typeof(stylesheets) == 'string'){
23168             Roo.get(this.iframe.contentDocument.head).createChild({
23169                 tag : 'link',
23170                 rel : 'stylesheet',
23171                 type : 'text/css',
23172                 href : stylesheets
23173             });
23174             
23175             return;
23176         }
23177         var _this = this;
23178      
23179         Roo.each(stylesheets, function(s) {
23180             if(!s.length){
23181                 return;
23182             }
23183             
23184             Roo.get(_this.iframe.contentDocument.head).createChild({
23185                 tag : 'link',
23186                 rel : 'stylesheet',
23187                 type : 'text/css',
23188                 href : s
23189             });
23190         });
23191
23192         
23193     },
23194     
23195     removeStylesheets : function()
23196     {
23197         var _this = this;
23198         
23199         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23200             s.remove();
23201         });
23202     },
23203     
23204     setStyle : function(style)
23205     {
23206         Roo.get(this.iframe.contentDocument.head).createChild({
23207             tag : 'style',
23208             type : 'text/css',
23209             html : style
23210         });
23211
23212         return;
23213     }
23214     
23215     // hide stuff that is not compatible
23216     /**
23217      * @event blur
23218      * @hide
23219      */
23220     /**
23221      * @event change
23222      * @hide
23223      */
23224     /**
23225      * @event focus
23226      * @hide
23227      */
23228     /**
23229      * @event specialkey
23230      * @hide
23231      */
23232     /**
23233      * @cfg {String} fieldClass @hide
23234      */
23235     /**
23236      * @cfg {String} focusClass @hide
23237      */
23238     /**
23239      * @cfg {String} autoCreate @hide
23240      */
23241     /**
23242      * @cfg {String} inputType @hide
23243      */
23244     /**
23245      * @cfg {String} invalidClass @hide
23246      */
23247     /**
23248      * @cfg {String} invalidText @hide
23249      */
23250     /**
23251      * @cfg {String} msgFx @hide
23252      */
23253     /**
23254      * @cfg {String} validateOnBlur @hide
23255      */
23256 });
23257
23258 Roo.HtmlEditorCore.white = [
23259         'area', 'br', 'img', 'input', 'hr', 'wbr',
23260         
23261        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23262        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23263        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23264        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23265        'table',   'ul',         'xmp', 
23266        
23267        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23268       'thead',   'tr', 
23269      
23270       'dir', 'menu', 'ol', 'ul', 'dl',
23271        
23272       'embed',  'object'
23273 ];
23274
23275
23276 Roo.HtmlEditorCore.black = [
23277     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23278         'applet', // 
23279         'base',   'basefont', 'bgsound', 'blink',  'body', 
23280         'frame',  'frameset', 'head',    'html',   'ilayer', 
23281         'iframe', 'layer',  'link',     'meta',    'object',   
23282         'script', 'style' ,'title',  'xml' // clean later..
23283 ];
23284 Roo.HtmlEditorCore.clean = [
23285     'script', 'style', 'title', 'xml'
23286 ];
23287 Roo.HtmlEditorCore.remove = [
23288     'font'
23289 ];
23290 // attributes..
23291
23292 Roo.HtmlEditorCore.ablack = [
23293     'on'
23294 ];
23295     
23296 Roo.HtmlEditorCore.aclean = [ 
23297     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23298 ];
23299
23300 // protocols..
23301 Roo.HtmlEditorCore.pwhite= [
23302         'http',  'https',  'mailto'
23303 ];
23304
23305 // white listed style attributes.
23306 Roo.HtmlEditorCore.cwhite= [
23307       //  'text-align', /// default is to allow most things..
23308       
23309          
23310 //        'font-size'//??
23311 ];
23312
23313 // black listed style attributes.
23314 Roo.HtmlEditorCore.cblack= [
23315       //  'font-size' -- this can be set by the project 
23316 ];
23317
23318
23319 Roo.HtmlEditorCore.swapCodes   =[ 
23320     [    8211, "--" ], 
23321     [    8212, "--" ], 
23322     [    8216,  "'" ],  
23323     [    8217, "'" ],  
23324     [    8220, '"' ],  
23325     [    8221, '"' ],  
23326     [    8226, "*" ],  
23327     [    8230, "..." ]
23328 ]; 
23329
23330     /*
23331  * - LGPL
23332  *
23333  * HtmlEditor
23334  * 
23335  */
23336
23337 /**
23338  * @class Roo.bootstrap.HtmlEditor
23339  * @extends Roo.bootstrap.TextArea
23340  * Bootstrap HtmlEditor class
23341
23342  * @constructor
23343  * Create a new HtmlEditor
23344  * @param {Object} config The config object
23345  */
23346
23347 Roo.bootstrap.HtmlEditor = function(config){
23348     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23349     if (!this.toolbars) {
23350         this.toolbars = [];
23351     }
23352     
23353     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23354     this.addEvents({
23355             /**
23356              * @event initialize
23357              * Fires when the editor is fully initialized (including the iframe)
23358              * @param {HtmlEditor} this
23359              */
23360             initialize: true,
23361             /**
23362              * @event activate
23363              * Fires when the editor is first receives the focus. Any insertion must wait
23364              * until after this event.
23365              * @param {HtmlEditor} this
23366              */
23367             activate: true,
23368              /**
23369              * @event beforesync
23370              * Fires before the textarea is updated with content from the editor iframe. Return false
23371              * to cancel the sync.
23372              * @param {HtmlEditor} this
23373              * @param {String} html
23374              */
23375             beforesync: true,
23376              /**
23377              * @event beforepush
23378              * Fires before the iframe editor is updated with content from the textarea. Return false
23379              * to cancel the push.
23380              * @param {HtmlEditor} this
23381              * @param {String} html
23382              */
23383             beforepush: true,
23384              /**
23385              * @event sync
23386              * Fires when the textarea is updated with content from the editor iframe.
23387              * @param {HtmlEditor} this
23388              * @param {String} html
23389              */
23390             sync: true,
23391              /**
23392              * @event push
23393              * Fires when the iframe editor is updated with content from the textarea.
23394              * @param {HtmlEditor} this
23395              * @param {String} html
23396              */
23397             push: true,
23398              /**
23399              * @event editmodechange
23400              * Fires when the editor switches edit modes
23401              * @param {HtmlEditor} this
23402              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23403              */
23404             editmodechange: true,
23405             /**
23406              * @event editorevent
23407              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23408              * @param {HtmlEditor} this
23409              */
23410             editorevent: true,
23411             /**
23412              * @event firstfocus
23413              * Fires when on first focus - needed by toolbars..
23414              * @param {HtmlEditor} this
23415              */
23416             firstfocus: true,
23417             /**
23418              * @event autosave
23419              * Auto save the htmlEditor value as a file into Events
23420              * @param {HtmlEditor} this
23421              */
23422             autosave: true,
23423             /**
23424              * @event savedpreview
23425              * preview the saved version of htmlEditor
23426              * @param {HtmlEditor} this
23427              */
23428             savedpreview: true
23429         });
23430 };
23431
23432
23433 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23434     
23435     
23436       /**
23437      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23438      */
23439     toolbars : false,
23440     
23441      /**
23442     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23443     */
23444     btns : [],
23445    
23446      /**
23447      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23448      *                        Roo.resizable.
23449      */
23450     resizable : false,
23451      /**
23452      * @cfg {Number} height (in pixels)
23453      */   
23454     height: 300,
23455    /**
23456      * @cfg {Number} width (in pixels)
23457      */   
23458     width: false,
23459     
23460     /**
23461      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23462      * 
23463      */
23464     stylesheets: false,
23465     
23466     // id of frame..
23467     frameId: false,
23468     
23469     // private properties
23470     validationEvent : false,
23471     deferHeight: true,
23472     initialized : false,
23473     activated : false,
23474     
23475     onFocus : Roo.emptyFn,
23476     iframePad:3,
23477     hideMode:'offsets',
23478     
23479     tbContainer : false,
23480     
23481     bodyCls : '',
23482     
23483     toolbarContainer :function() {
23484         return this.wrap.select('.x-html-editor-tb',true).first();
23485     },
23486
23487     /**
23488      * Protected method that will not generally be called directly. It
23489      * is called when the editor creates its toolbar. Override this method if you need to
23490      * add custom toolbar buttons.
23491      * @param {HtmlEditor} editor
23492      */
23493     createToolbar : function(){
23494         Roo.log('renewing');
23495         Roo.log("create toolbars");
23496         
23497         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23498         this.toolbars[0].render(this.toolbarContainer());
23499         
23500         return;
23501         
23502 //        if (!editor.toolbars || !editor.toolbars.length) {
23503 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23504 //        }
23505 //        
23506 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23507 //            editor.toolbars[i] = Roo.factory(
23508 //                    typeof(editor.toolbars[i]) == 'string' ?
23509 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23510 //                Roo.bootstrap.HtmlEditor);
23511 //            editor.toolbars[i].init(editor);
23512 //        }
23513     },
23514
23515      
23516     // private
23517     onRender : function(ct, position)
23518     {
23519        // Roo.log("Call onRender: " + this.xtype);
23520         var _t = this;
23521         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23522       
23523         this.wrap = this.inputEl().wrap({
23524             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23525         });
23526         
23527         this.editorcore.onRender(ct, position);
23528          
23529         if (this.resizable) {
23530             this.resizeEl = new Roo.Resizable(this.wrap, {
23531                 pinned : true,
23532                 wrap: true,
23533                 dynamic : true,
23534                 minHeight : this.height,
23535                 height: this.height,
23536                 handles : this.resizable,
23537                 width: this.width,
23538                 listeners : {
23539                     resize : function(r, w, h) {
23540                         _t.onResize(w,h); // -something
23541                     }
23542                 }
23543             });
23544             
23545         }
23546         this.createToolbar(this);
23547        
23548         
23549         if(!this.width && this.resizable){
23550             this.setSize(this.wrap.getSize());
23551         }
23552         if (this.resizeEl) {
23553             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23554             // should trigger onReize..
23555         }
23556         
23557     },
23558
23559     // private
23560     onResize : function(w, h)
23561     {
23562         Roo.log('resize: ' +w + ',' + h );
23563         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23564         var ew = false;
23565         var eh = false;
23566         
23567         if(this.inputEl() ){
23568             if(typeof w == 'number'){
23569                 var aw = w - this.wrap.getFrameWidth('lr');
23570                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23571                 ew = aw;
23572             }
23573             if(typeof h == 'number'){
23574                  var tbh = -11;  // fixme it needs to tool bar size!
23575                 for (var i =0; i < this.toolbars.length;i++) {
23576                     // fixme - ask toolbars for heights?
23577                     tbh += this.toolbars[i].el.getHeight();
23578                     //if (this.toolbars[i].footer) {
23579                     //    tbh += this.toolbars[i].footer.el.getHeight();
23580                     //}
23581                 }
23582               
23583                 
23584                 
23585                 
23586                 
23587                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23588                 ah -= 5; // knock a few pixes off for look..
23589                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23590                 var eh = ah;
23591             }
23592         }
23593         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23594         this.editorcore.onResize(ew,eh);
23595         
23596     },
23597
23598     /**
23599      * Toggles the editor between standard and source edit mode.
23600      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23601      */
23602     toggleSourceEdit : function(sourceEditMode)
23603     {
23604         this.editorcore.toggleSourceEdit(sourceEditMode);
23605         
23606         if(this.editorcore.sourceEditMode){
23607             Roo.log('editor - showing textarea');
23608             
23609 //            Roo.log('in');
23610 //            Roo.log(this.syncValue());
23611             this.syncValue();
23612             this.inputEl().removeClass(['hide', 'x-hidden']);
23613             this.inputEl().dom.removeAttribute('tabIndex');
23614             this.inputEl().focus();
23615         }else{
23616             Roo.log('editor - hiding textarea');
23617 //            Roo.log('out')
23618 //            Roo.log(this.pushValue()); 
23619             this.pushValue();
23620             
23621             this.inputEl().addClass(['hide', 'x-hidden']);
23622             this.inputEl().dom.setAttribute('tabIndex', -1);
23623             //this.deferFocus();
23624         }
23625          
23626         if(this.resizable){
23627             this.setSize(this.wrap.getSize());
23628         }
23629         
23630         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23631     },
23632  
23633     // private (for BoxComponent)
23634     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23635
23636     // private (for BoxComponent)
23637     getResizeEl : function(){
23638         return this.wrap;
23639     },
23640
23641     // private (for BoxComponent)
23642     getPositionEl : function(){
23643         return this.wrap;
23644     },
23645
23646     // private
23647     initEvents : function(){
23648         this.originalValue = this.getValue();
23649     },
23650
23651 //    /**
23652 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23653 //     * @method
23654 //     */
23655 //    markInvalid : Roo.emptyFn,
23656 //    /**
23657 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23658 //     * @method
23659 //     */
23660 //    clearInvalid : Roo.emptyFn,
23661
23662     setValue : function(v){
23663         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23664         this.editorcore.pushValue();
23665     },
23666
23667      
23668     // private
23669     deferFocus : function(){
23670         this.focus.defer(10, this);
23671     },
23672
23673     // doc'ed in Field
23674     focus : function(){
23675         this.editorcore.focus();
23676         
23677     },
23678       
23679
23680     // private
23681     onDestroy : function(){
23682         
23683         
23684         
23685         if(this.rendered){
23686             
23687             for (var i =0; i < this.toolbars.length;i++) {
23688                 // fixme - ask toolbars for heights?
23689                 this.toolbars[i].onDestroy();
23690             }
23691             
23692             this.wrap.dom.innerHTML = '';
23693             this.wrap.remove();
23694         }
23695     },
23696
23697     // private
23698     onFirstFocus : function(){
23699         //Roo.log("onFirstFocus");
23700         this.editorcore.onFirstFocus();
23701          for (var i =0; i < this.toolbars.length;i++) {
23702             this.toolbars[i].onFirstFocus();
23703         }
23704         
23705     },
23706     
23707     // private
23708     syncValue : function()
23709     {   
23710         this.editorcore.syncValue();
23711     },
23712     
23713     pushValue : function()
23714     {   
23715         this.editorcore.pushValue();
23716     }
23717      
23718     
23719     // hide stuff that is not compatible
23720     /**
23721      * @event blur
23722      * @hide
23723      */
23724     /**
23725      * @event change
23726      * @hide
23727      */
23728     /**
23729      * @event focus
23730      * @hide
23731      */
23732     /**
23733      * @event specialkey
23734      * @hide
23735      */
23736     /**
23737      * @cfg {String} fieldClass @hide
23738      */
23739     /**
23740      * @cfg {String} focusClass @hide
23741      */
23742     /**
23743      * @cfg {String} autoCreate @hide
23744      */
23745     /**
23746      * @cfg {String} inputType @hide
23747      */
23748     /**
23749      * @cfg {String} invalidClass @hide
23750      */
23751     /**
23752      * @cfg {String} invalidText @hide
23753      */
23754     /**
23755      * @cfg {String} msgFx @hide
23756      */
23757     /**
23758      * @cfg {String} validateOnBlur @hide
23759      */
23760 });
23761  
23762     
23763    
23764    
23765    
23766       
23767 Roo.namespace('Roo.bootstrap.htmleditor');
23768 /**
23769  * @class Roo.bootstrap.HtmlEditorToolbar1
23770  * Basic Toolbar
23771  * 
23772  * Usage:
23773  *
23774  new Roo.bootstrap.HtmlEditor({
23775     ....
23776     toolbars : [
23777         new Roo.bootstrap.HtmlEditorToolbar1({
23778             disable : { fonts: 1 , format: 1, ..., ... , ...],
23779             btns : [ .... ]
23780         })
23781     }
23782      
23783  * 
23784  * @cfg {Object} disable List of elements to disable..
23785  * @cfg {Array} btns List of additional buttons.
23786  * 
23787  * 
23788  * NEEDS Extra CSS? 
23789  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23790  */
23791  
23792 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23793 {
23794     
23795     Roo.apply(this, config);
23796     
23797     // default disabled, based on 'good practice'..
23798     this.disable = this.disable || {};
23799     Roo.applyIf(this.disable, {
23800         fontSize : true,
23801         colors : true,
23802         specialElements : true
23803     });
23804     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23805     
23806     this.editor = config.editor;
23807     this.editorcore = config.editor.editorcore;
23808     
23809     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23810     
23811     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23812     // dont call parent... till later.
23813 }
23814 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23815      
23816     bar : true,
23817     
23818     editor : false,
23819     editorcore : false,
23820     
23821     
23822     formats : [
23823         "p" ,  
23824         "h1","h2","h3","h4","h5","h6", 
23825         "pre", "code", 
23826         "abbr", "acronym", "address", "cite", "samp", "var",
23827         'div','span'
23828     ],
23829     
23830     onRender : function(ct, position)
23831     {
23832        // Roo.log("Call onRender: " + this.xtype);
23833         
23834        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23835        Roo.log(this.el);
23836        this.el.dom.style.marginBottom = '0';
23837        var _this = this;
23838        var editorcore = this.editorcore;
23839        var editor= this.editor;
23840        
23841        var children = [];
23842        var btn = function(id,cmd , toggle, handler, html){
23843        
23844             var  event = toggle ? 'toggle' : 'click';
23845        
23846             var a = {
23847                 size : 'sm',
23848                 xtype: 'Button',
23849                 xns: Roo.bootstrap,
23850                 glyphicon : id,
23851                 cmd : id || cmd,
23852                 enableToggle:toggle !== false,
23853                 html : html || '',
23854                 pressed : toggle ? false : null,
23855                 listeners : {}
23856             };
23857             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23858                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23859             };
23860             children.push(a);
23861             return a;
23862        }
23863        
23864     //    var cb_box = function...
23865         
23866         var style = {
23867                 xtype: 'Button',
23868                 size : 'sm',
23869                 xns: Roo.bootstrap,
23870                 glyphicon : 'font',
23871                 //html : 'submit'
23872                 menu : {
23873                     xtype: 'Menu',
23874                     xns: Roo.bootstrap,
23875                     items:  []
23876                 }
23877         };
23878         Roo.each(this.formats, function(f) {
23879             style.menu.items.push({
23880                 xtype :'MenuItem',
23881                 xns: Roo.bootstrap,
23882                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23883                 tagname : f,
23884                 listeners : {
23885                     click : function()
23886                     {
23887                         editorcore.insertTag(this.tagname);
23888                         editor.focus();
23889                     }
23890                 }
23891                 
23892             });
23893         });
23894         children.push(style);   
23895         
23896         btn('bold',false,true);
23897         btn('italic',false,true);
23898         btn('align-left', 'justifyleft',true);
23899         btn('align-center', 'justifycenter',true);
23900         btn('align-right' , 'justifyright',true);
23901         btn('link', false, false, function(btn) {
23902             //Roo.log("create link?");
23903             var url = prompt(this.createLinkText, this.defaultLinkValue);
23904             if(url && url != 'http:/'+'/'){
23905                 this.editorcore.relayCmd('createlink', url);
23906             }
23907         }),
23908         btn('list','insertunorderedlist',true);
23909         btn('pencil', false,true, function(btn){
23910                 Roo.log(this);
23911                 this.toggleSourceEdit(btn.pressed);
23912         });
23913         
23914         if (this.editor.btns.length > 0) {
23915             for (var i = 0; i<this.editor.btns.length; i++) {
23916                 children.push(this.editor.btns[i]);
23917             }
23918         }
23919         
23920         /*
23921         var cog = {
23922                 xtype: 'Button',
23923                 size : 'sm',
23924                 xns: Roo.bootstrap,
23925                 glyphicon : 'cog',
23926                 //html : 'submit'
23927                 menu : {
23928                     xtype: 'Menu',
23929                     xns: Roo.bootstrap,
23930                     items:  []
23931                 }
23932         };
23933         
23934         cog.menu.items.push({
23935             xtype :'MenuItem',
23936             xns: Roo.bootstrap,
23937             html : Clean styles,
23938             tagname : f,
23939             listeners : {
23940                 click : function()
23941                 {
23942                     editorcore.insertTag(this.tagname);
23943                     editor.focus();
23944                 }
23945             }
23946             
23947         });
23948        */
23949         
23950          
23951        this.xtype = 'NavSimplebar';
23952         
23953         for(var i=0;i< children.length;i++) {
23954             
23955             this.buttons.add(this.addxtypeChild(children[i]));
23956             
23957         }
23958         
23959         editor.on('editorevent', this.updateToolbar, this);
23960     },
23961     onBtnClick : function(id)
23962     {
23963        this.editorcore.relayCmd(id);
23964        this.editorcore.focus();
23965     },
23966     
23967     /**
23968      * Protected method that will not generally be called directly. It triggers
23969      * a toolbar update by reading the markup state of the current selection in the editor.
23970      */
23971     updateToolbar: function(){
23972
23973         if(!this.editorcore.activated){
23974             this.editor.onFirstFocus(); // is this neeed?
23975             return;
23976         }
23977
23978         var btns = this.buttons; 
23979         var doc = this.editorcore.doc;
23980         btns.get('bold').setActive(doc.queryCommandState('bold'));
23981         btns.get('italic').setActive(doc.queryCommandState('italic'));
23982         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23983         
23984         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23985         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23986         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23987         
23988         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23989         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23990          /*
23991         
23992         var ans = this.editorcore.getAllAncestors();
23993         if (this.formatCombo) {
23994             
23995             
23996             var store = this.formatCombo.store;
23997             this.formatCombo.setValue("");
23998             for (var i =0; i < ans.length;i++) {
23999                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24000                     // select it..
24001                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24002                     break;
24003                 }
24004             }
24005         }
24006         
24007         
24008         
24009         // hides menus... - so this cant be on a menu...
24010         Roo.bootstrap.MenuMgr.hideAll();
24011         */
24012         Roo.bootstrap.MenuMgr.hideAll();
24013         //this.editorsyncValue();
24014     },
24015     onFirstFocus: function() {
24016         this.buttons.each(function(item){
24017            item.enable();
24018         });
24019     },
24020     toggleSourceEdit : function(sourceEditMode){
24021         
24022           
24023         if(sourceEditMode){
24024             Roo.log("disabling buttons");
24025            this.buttons.each( function(item){
24026                 if(item.cmd != 'pencil'){
24027                     item.disable();
24028                 }
24029             });
24030           
24031         }else{
24032             Roo.log("enabling buttons");
24033             if(this.editorcore.initialized){
24034                 this.buttons.each( function(item){
24035                     item.enable();
24036                 });
24037             }
24038             
24039         }
24040         Roo.log("calling toggole on editor");
24041         // tell the editor that it's been pressed..
24042         this.editor.toggleSourceEdit(sourceEditMode);
24043        
24044     }
24045 });
24046
24047
24048
24049
24050
24051 /**
24052  * @class Roo.bootstrap.Table.AbstractSelectionModel
24053  * @extends Roo.util.Observable
24054  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24055  * implemented by descendant classes.  This class should not be directly instantiated.
24056  * @constructor
24057  */
24058 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24059     this.locked = false;
24060     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24061 };
24062
24063
24064 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24065     /** @ignore Called by the grid automatically. Do not call directly. */
24066     init : function(grid){
24067         this.grid = grid;
24068         this.initEvents();
24069     },
24070
24071     /**
24072      * Locks the selections.
24073      */
24074     lock : function(){
24075         this.locked = true;
24076     },
24077
24078     /**
24079      * Unlocks the selections.
24080      */
24081     unlock : function(){
24082         this.locked = false;
24083     },
24084
24085     /**
24086      * Returns true if the selections are locked.
24087      * @return {Boolean}
24088      */
24089     isLocked : function(){
24090         return this.locked;
24091     }
24092 });
24093 /**
24094  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24095  * @class Roo.bootstrap.Table.RowSelectionModel
24096  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24097  * It supports multiple selections and keyboard selection/navigation. 
24098  * @constructor
24099  * @param {Object} config
24100  */
24101
24102 Roo.bootstrap.Table.RowSelectionModel = function(config){
24103     Roo.apply(this, config);
24104     this.selections = new Roo.util.MixedCollection(false, function(o){
24105         return o.id;
24106     });
24107
24108     this.last = false;
24109     this.lastActive = false;
24110
24111     this.addEvents({
24112         /**
24113              * @event selectionchange
24114              * Fires when the selection changes
24115              * @param {SelectionModel} this
24116              */
24117             "selectionchange" : true,
24118         /**
24119              * @event afterselectionchange
24120              * Fires after the selection changes (eg. by key press or clicking)
24121              * @param {SelectionModel} this
24122              */
24123             "afterselectionchange" : true,
24124         /**
24125              * @event beforerowselect
24126              * Fires when a row is selected being selected, return false to cancel.
24127              * @param {SelectionModel} this
24128              * @param {Number} rowIndex The selected index
24129              * @param {Boolean} keepExisting False if other selections will be cleared
24130              */
24131             "beforerowselect" : true,
24132         /**
24133              * @event rowselect
24134              * Fires when a row is selected.
24135              * @param {SelectionModel} this
24136              * @param {Number} rowIndex The selected index
24137              * @param {Roo.data.Record} r The record
24138              */
24139             "rowselect" : true,
24140         /**
24141              * @event rowdeselect
24142              * Fires when a row is deselected.
24143              * @param {SelectionModel} this
24144              * @param {Number} rowIndex The selected index
24145              */
24146         "rowdeselect" : true
24147     });
24148     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24149     this.locked = false;
24150  };
24151
24152 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24153     /**
24154      * @cfg {Boolean} singleSelect
24155      * True to allow selection of only one row at a time (defaults to false)
24156      */
24157     singleSelect : false,
24158
24159     // private
24160     initEvents : function()
24161     {
24162
24163         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24164         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24165         //}else{ // allow click to work like normal
24166          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24167         //}
24168         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24169         this.grid.on("rowclick", this.handleMouseDown, this);
24170         
24171         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24172             "up" : function(e){
24173                 if(!e.shiftKey){
24174                     this.selectPrevious(e.shiftKey);
24175                 }else if(this.last !== false && this.lastActive !== false){
24176                     var last = this.last;
24177                     this.selectRange(this.last,  this.lastActive-1);
24178                     this.grid.getView().focusRow(this.lastActive);
24179                     if(last !== false){
24180                         this.last = last;
24181                     }
24182                 }else{
24183                     this.selectFirstRow();
24184                 }
24185                 this.fireEvent("afterselectionchange", this);
24186             },
24187             "down" : function(e){
24188                 if(!e.shiftKey){
24189                     this.selectNext(e.shiftKey);
24190                 }else if(this.last !== false && this.lastActive !== false){
24191                     var last = this.last;
24192                     this.selectRange(this.last,  this.lastActive+1);
24193                     this.grid.getView().focusRow(this.lastActive);
24194                     if(last !== false){
24195                         this.last = last;
24196                     }
24197                 }else{
24198                     this.selectFirstRow();
24199                 }
24200                 this.fireEvent("afterselectionchange", this);
24201             },
24202             scope: this
24203         });
24204         this.grid.store.on('load', function(){
24205             this.selections.clear();
24206         },this);
24207         /*
24208         var view = this.grid.view;
24209         view.on("refresh", this.onRefresh, this);
24210         view.on("rowupdated", this.onRowUpdated, this);
24211         view.on("rowremoved", this.onRemove, this);
24212         */
24213     },
24214
24215     // private
24216     onRefresh : function()
24217     {
24218         var ds = this.grid.store, i, v = this.grid.view;
24219         var s = this.selections;
24220         s.each(function(r){
24221             if((i = ds.indexOfId(r.id)) != -1){
24222                 v.onRowSelect(i);
24223             }else{
24224                 s.remove(r);
24225             }
24226         });
24227     },
24228
24229     // private
24230     onRemove : function(v, index, r){
24231         this.selections.remove(r);
24232     },
24233
24234     // private
24235     onRowUpdated : function(v, index, r){
24236         if(this.isSelected(r)){
24237             v.onRowSelect(index);
24238         }
24239     },
24240
24241     /**
24242      * Select records.
24243      * @param {Array} records The records to select
24244      * @param {Boolean} keepExisting (optional) True to keep existing selections
24245      */
24246     selectRecords : function(records, keepExisting)
24247     {
24248         if(!keepExisting){
24249             this.clearSelections();
24250         }
24251             var ds = this.grid.store;
24252         for(var i = 0, len = records.length; i < len; i++){
24253             this.selectRow(ds.indexOf(records[i]), true);
24254         }
24255     },
24256
24257     /**
24258      * Gets the number of selected rows.
24259      * @return {Number}
24260      */
24261     getCount : function(){
24262         return this.selections.length;
24263     },
24264
24265     /**
24266      * Selects the first row in the grid.
24267      */
24268     selectFirstRow : function(){
24269         this.selectRow(0);
24270     },
24271
24272     /**
24273      * Select the last row.
24274      * @param {Boolean} keepExisting (optional) True to keep existing selections
24275      */
24276     selectLastRow : function(keepExisting){
24277         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24278         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24279     },
24280
24281     /**
24282      * Selects the row immediately following the last selected row.
24283      * @param {Boolean} keepExisting (optional) True to keep existing selections
24284      */
24285     selectNext : function(keepExisting)
24286     {
24287             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24288             this.selectRow(this.last+1, keepExisting);
24289             this.grid.getView().focusRow(this.last);
24290         }
24291     },
24292
24293     /**
24294      * Selects the row that precedes the last selected row.
24295      * @param {Boolean} keepExisting (optional) True to keep existing selections
24296      */
24297     selectPrevious : function(keepExisting){
24298         if(this.last){
24299             this.selectRow(this.last-1, keepExisting);
24300             this.grid.getView().focusRow(this.last);
24301         }
24302     },
24303
24304     /**
24305      * Returns the selected records
24306      * @return {Array} Array of selected records
24307      */
24308     getSelections : function(){
24309         return [].concat(this.selections.items);
24310     },
24311
24312     /**
24313      * Returns the first selected record.
24314      * @return {Record}
24315      */
24316     getSelected : function(){
24317         return this.selections.itemAt(0);
24318     },
24319
24320
24321     /**
24322      * Clears all selections.
24323      */
24324     clearSelections : function(fast)
24325     {
24326         if(this.locked) {
24327             return;
24328         }
24329         if(fast !== true){
24330                 var ds = this.grid.store;
24331             var s = this.selections;
24332             s.each(function(r){
24333                 this.deselectRow(ds.indexOfId(r.id));
24334             }, this);
24335             s.clear();
24336         }else{
24337             this.selections.clear();
24338         }
24339         this.last = false;
24340     },
24341
24342
24343     /**
24344      * Selects all rows.
24345      */
24346     selectAll : function(){
24347         if(this.locked) {
24348             return;
24349         }
24350         this.selections.clear();
24351         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24352             this.selectRow(i, true);
24353         }
24354     },
24355
24356     /**
24357      * Returns True if there is a selection.
24358      * @return {Boolean}
24359      */
24360     hasSelection : function(){
24361         return this.selections.length > 0;
24362     },
24363
24364     /**
24365      * Returns True if the specified row is selected.
24366      * @param {Number/Record} record The record or index of the record to check
24367      * @return {Boolean}
24368      */
24369     isSelected : function(index){
24370             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24371         return (r && this.selections.key(r.id) ? true : false);
24372     },
24373
24374     /**
24375      * Returns True if the specified record id is selected.
24376      * @param {String} id The id of record to check
24377      * @return {Boolean}
24378      */
24379     isIdSelected : function(id){
24380         return (this.selections.key(id) ? true : false);
24381     },
24382
24383
24384     // private
24385     handleMouseDBClick : function(e, t){
24386         
24387     },
24388     // private
24389     handleMouseDown : function(e, t)
24390     {
24391             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24392         if(this.isLocked() || rowIndex < 0 ){
24393             return;
24394         };
24395         if(e.shiftKey && this.last !== false){
24396             var last = this.last;
24397             this.selectRange(last, rowIndex, e.ctrlKey);
24398             this.last = last; // reset the last
24399             t.focus();
24400     
24401         }else{
24402             var isSelected = this.isSelected(rowIndex);
24403             //Roo.log("select row:" + rowIndex);
24404             if(isSelected){
24405                 this.deselectRow(rowIndex);
24406             } else {
24407                         this.selectRow(rowIndex, true);
24408             }
24409     
24410             /*
24411                 if(e.button !== 0 && isSelected){
24412                 alert('rowIndex 2: ' + rowIndex);
24413                     view.focusRow(rowIndex);
24414                 }else if(e.ctrlKey && isSelected){
24415                     this.deselectRow(rowIndex);
24416                 }else if(!isSelected){
24417                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24418                     view.focusRow(rowIndex);
24419                 }
24420             */
24421         }
24422         this.fireEvent("afterselectionchange", this);
24423     },
24424     // private
24425     handleDragableRowClick :  function(grid, rowIndex, e) 
24426     {
24427         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24428             this.selectRow(rowIndex, false);
24429             grid.view.focusRow(rowIndex);
24430              this.fireEvent("afterselectionchange", this);
24431         }
24432     },
24433     
24434     /**
24435      * Selects multiple rows.
24436      * @param {Array} rows Array of the indexes of the row to select
24437      * @param {Boolean} keepExisting (optional) True to keep existing selections
24438      */
24439     selectRows : function(rows, keepExisting){
24440         if(!keepExisting){
24441             this.clearSelections();
24442         }
24443         for(var i = 0, len = rows.length; i < len; i++){
24444             this.selectRow(rows[i], true);
24445         }
24446     },
24447
24448     /**
24449      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24450      * @param {Number} startRow The index of the first row in the range
24451      * @param {Number} endRow The index of the last row in the range
24452      * @param {Boolean} keepExisting (optional) True to retain existing selections
24453      */
24454     selectRange : function(startRow, endRow, keepExisting){
24455         if(this.locked) {
24456             return;
24457         }
24458         if(!keepExisting){
24459             this.clearSelections();
24460         }
24461         if(startRow <= endRow){
24462             for(var i = startRow; i <= endRow; i++){
24463                 this.selectRow(i, true);
24464             }
24465         }else{
24466             for(var i = startRow; i >= endRow; i--){
24467                 this.selectRow(i, true);
24468             }
24469         }
24470     },
24471
24472     /**
24473      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24474      * @param {Number} startRow The index of the first row in the range
24475      * @param {Number} endRow The index of the last row in the range
24476      */
24477     deselectRange : function(startRow, endRow, preventViewNotify){
24478         if(this.locked) {
24479             return;
24480         }
24481         for(var i = startRow; i <= endRow; i++){
24482             this.deselectRow(i, preventViewNotify);
24483         }
24484     },
24485
24486     /**
24487      * Selects a row.
24488      * @param {Number} row The index of the row to select
24489      * @param {Boolean} keepExisting (optional) True to keep existing selections
24490      */
24491     selectRow : function(index, keepExisting, preventViewNotify)
24492     {
24493             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24494             return;
24495         }
24496         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24497             if(!keepExisting || this.singleSelect){
24498                 this.clearSelections();
24499             }
24500             
24501             var r = this.grid.store.getAt(index);
24502             //console.log('selectRow - record id :' + r.id);
24503             
24504             this.selections.add(r);
24505             this.last = this.lastActive = index;
24506             if(!preventViewNotify){
24507                 var proxy = new Roo.Element(
24508                                 this.grid.getRowDom(index)
24509                 );
24510                 proxy.addClass('bg-info info');
24511             }
24512             this.fireEvent("rowselect", this, index, r);
24513             this.fireEvent("selectionchange", this);
24514         }
24515     },
24516
24517     /**
24518      * Deselects a row.
24519      * @param {Number} row The index of the row to deselect
24520      */
24521     deselectRow : function(index, preventViewNotify)
24522     {
24523         if(this.locked) {
24524             return;
24525         }
24526         if(this.last == index){
24527             this.last = false;
24528         }
24529         if(this.lastActive == index){
24530             this.lastActive = false;
24531         }
24532         
24533         var r = this.grid.store.getAt(index);
24534         if (!r) {
24535             return;
24536         }
24537         
24538         this.selections.remove(r);
24539         //.console.log('deselectRow - record id :' + r.id);
24540         if(!preventViewNotify){
24541         
24542             var proxy = new Roo.Element(
24543                 this.grid.getRowDom(index)
24544             );
24545             proxy.removeClass('bg-info info');
24546         }
24547         this.fireEvent("rowdeselect", this, index);
24548         this.fireEvent("selectionchange", this);
24549     },
24550
24551     // private
24552     restoreLast : function(){
24553         if(this._last){
24554             this.last = this._last;
24555         }
24556     },
24557
24558     // private
24559     acceptsNav : function(row, col, cm){
24560         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24561     },
24562
24563     // private
24564     onEditorKey : function(field, e){
24565         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24566         if(k == e.TAB){
24567             e.stopEvent();
24568             ed.completeEdit();
24569             if(e.shiftKey){
24570                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24571             }else{
24572                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24573             }
24574         }else if(k == e.ENTER && !e.ctrlKey){
24575             e.stopEvent();
24576             ed.completeEdit();
24577             if(e.shiftKey){
24578                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24579             }else{
24580                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24581             }
24582         }else if(k == e.ESC){
24583             ed.cancelEdit();
24584         }
24585         if(newCell){
24586             g.startEditing(newCell[0], newCell[1]);
24587         }
24588     }
24589 });
24590 /*
24591  * Based on:
24592  * Ext JS Library 1.1.1
24593  * Copyright(c) 2006-2007, Ext JS, LLC.
24594  *
24595  * Originally Released Under LGPL - original licence link has changed is not relivant.
24596  *
24597  * Fork - LGPL
24598  * <script type="text/javascript">
24599  */
24600  
24601 /**
24602  * @class Roo.bootstrap.PagingToolbar
24603  * @extends Roo.bootstrap.NavSimplebar
24604  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24605  * @constructor
24606  * Create a new PagingToolbar
24607  * @param {Object} config The config object
24608  * @param {Roo.data.Store} store
24609  */
24610 Roo.bootstrap.PagingToolbar = function(config)
24611 {
24612     // old args format still supported... - xtype is prefered..
24613         // created from xtype...
24614     
24615     this.ds = config.dataSource;
24616     
24617     if (config.store && !this.ds) {
24618         this.store= Roo.factory(config.store, Roo.data);
24619         this.ds = this.store;
24620         this.ds.xmodule = this.xmodule || false;
24621     }
24622     
24623     this.toolbarItems = [];
24624     if (config.items) {
24625         this.toolbarItems = config.items;
24626     }
24627     
24628     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24629     
24630     this.cursor = 0;
24631     
24632     if (this.ds) { 
24633         this.bind(this.ds);
24634     }
24635     
24636     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24637     
24638 };
24639
24640 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24641     /**
24642      * @cfg {Roo.data.Store} dataSource
24643      * The underlying data store providing the paged data
24644      */
24645     /**
24646      * @cfg {String/HTMLElement/Element} container
24647      * container The id or element that will contain the toolbar
24648      */
24649     /**
24650      * @cfg {Boolean} displayInfo
24651      * True to display the displayMsg (defaults to false)
24652      */
24653     /**
24654      * @cfg {Number} pageSize
24655      * The number of records to display per page (defaults to 20)
24656      */
24657     pageSize: 20,
24658     /**
24659      * @cfg {String} displayMsg
24660      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24661      */
24662     displayMsg : 'Displaying {0} - {1} of {2}',
24663     /**
24664      * @cfg {String} emptyMsg
24665      * The message to display when no records are found (defaults to "No data to display")
24666      */
24667     emptyMsg : 'No data to display',
24668     /**
24669      * Customizable piece of the default paging text (defaults to "Page")
24670      * @type String
24671      */
24672     beforePageText : "Page",
24673     /**
24674      * Customizable piece of the default paging text (defaults to "of %0")
24675      * @type String
24676      */
24677     afterPageText : "of {0}",
24678     /**
24679      * Customizable piece of the default paging text (defaults to "First Page")
24680      * @type String
24681      */
24682     firstText : "First Page",
24683     /**
24684      * Customizable piece of the default paging text (defaults to "Previous Page")
24685      * @type String
24686      */
24687     prevText : "Previous Page",
24688     /**
24689      * Customizable piece of the default paging text (defaults to "Next Page")
24690      * @type String
24691      */
24692     nextText : "Next Page",
24693     /**
24694      * Customizable piece of the default paging text (defaults to "Last Page")
24695      * @type String
24696      */
24697     lastText : "Last Page",
24698     /**
24699      * Customizable piece of the default paging text (defaults to "Refresh")
24700      * @type String
24701      */
24702     refreshText : "Refresh",
24703
24704     buttons : false,
24705     // private
24706     onRender : function(ct, position) 
24707     {
24708         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24709         this.navgroup.parentId = this.id;
24710         this.navgroup.onRender(this.el, null);
24711         // add the buttons to the navgroup
24712         
24713         if(this.displayInfo){
24714             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24715             this.displayEl = this.el.select('.x-paging-info', true).first();
24716 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24717 //            this.displayEl = navel.el.select('span',true).first();
24718         }
24719         
24720         var _this = this;
24721         
24722         if(this.buttons){
24723             Roo.each(_this.buttons, function(e){ // this might need to use render????
24724                Roo.factory(e).render(_this.el);
24725             });
24726         }
24727             
24728         Roo.each(_this.toolbarItems, function(e) {
24729             _this.navgroup.addItem(e);
24730         });
24731         
24732         
24733         this.first = this.navgroup.addItem({
24734             tooltip: this.firstText,
24735             cls: "prev",
24736             icon : 'fa fa-step-backward',
24737             disabled: true,
24738             preventDefault: true,
24739             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24740         });
24741         
24742         this.prev =  this.navgroup.addItem({
24743             tooltip: this.prevText,
24744             cls: "prev",
24745             icon : 'fa fa-backward',
24746             disabled: true,
24747             preventDefault: true,
24748             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24749         });
24750     //this.addSeparator();
24751         
24752         
24753         var field = this.navgroup.addItem( {
24754             tagtype : 'span',
24755             cls : 'x-paging-position',
24756             
24757             html : this.beforePageText  +
24758                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24759                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24760          } ); //?? escaped?
24761         
24762         this.field = field.el.select('input', true).first();
24763         this.field.on("keydown", this.onPagingKeydown, this);
24764         this.field.on("focus", function(){this.dom.select();});
24765     
24766     
24767         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24768         //this.field.setHeight(18);
24769         //this.addSeparator();
24770         this.next = this.navgroup.addItem({
24771             tooltip: this.nextText,
24772             cls: "next",
24773             html : ' <i class="fa fa-forward">',
24774             disabled: true,
24775             preventDefault: true,
24776             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24777         });
24778         this.last = this.navgroup.addItem({
24779             tooltip: this.lastText,
24780             icon : 'fa fa-step-forward',
24781             cls: "next",
24782             disabled: true,
24783             preventDefault: true,
24784             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24785         });
24786     //this.addSeparator();
24787         this.loading = this.navgroup.addItem({
24788             tooltip: this.refreshText,
24789             icon: 'fa fa-refresh',
24790             preventDefault: true,
24791             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24792         });
24793         
24794     },
24795
24796     // private
24797     updateInfo : function(){
24798         if(this.displayEl){
24799             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24800             var msg = count == 0 ?
24801                 this.emptyMsg :
24802                 String.format(
24803                     this.displayMsg,
24804                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24805                 );
24806             this.displayEl.update(msg);
24807         }
24808     },
24809
24810     // private
24811     onLoad : function(ds, r, o)
24812     {
24813         this.cursor = o.params.start ? o.params.start : 0;
24814         
24815         var d = this.getPageData(),
24816             ap = d.activePage,
24817             ps = d.pages;
24818         
24819         
24820         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24821         this.field.dom.value = ap;
24822         this.first.setDisabled(ap == 1);
24823         this.prev.setDisabled(ap == 1);
24824         this.next.setDisabled(ap == ps);
24825         this.last.setDisabled(ap == ps);
24826         this.loading.enable();
24827         this.updateInfo();
24828     },
24829
24830     // private
24831     getPageData : function(){
24832         var total = this.ds.getTotalCount();
24833         return {
24834             total : total,
24835             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24836             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24837         };
24838     },
24839
24840     // private
24841     onLoadError : function(){
24842         this.loading.enable();
24843     },
24844
24845     // private
24846     onPagingKeydown : function(e){
24847         var k = e.getKey();
24848         var d = this.getPageData();
24849         if(k == e.RETURN){
24850             var v = this.field.dom.value, pageNum;
24851             if(!v || isNaN(pageNum = parseInt(v, 10))){
24852                 this.field.dom.value = d.activePage;
24853                 return;
24854             }
24855             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24856             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24857             e.stopEvent();
24858         }
24859         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))
24860         {
24861           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24862           this.field.dom.value = pageNum;
24863           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24864           e.stopEvent();
24865         }
24866         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24867         {
24868           var v = this.field.dom.value, pageNum; 
24869           var increment = (e.shiftKey) ? 10 : 1;
24870           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24871                 increment *= -1;
24872           }
24873           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24874             this.field.dom.value = d.activePage;
24875             return;
24876           }
24877           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24878           {
24879             this.field.dom.value = parseInt(v, 10) + increment;
24880             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24881             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24882           }
24883           e.stopEvent();
24884         }
24885     },
24886
24887     // private
24888     beforeLoad : function(){
24889         if(this.loading){
24890             this.loading.disable();
24891         }
24892     },
24893
24894     // private
24895     onClick : function(which){
24896         
24897         var ds = this.ds;
24898         if (!ds) {
24899             return;
24900         }
24901         
24902         switch(which){
24903             case "first":
24904                 ds.load({params:{start: 0, limit: this.pageSize}});
24905             break;
24906             case "prev":
24907                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24908             break;
24909             case "next":
24910                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24911             break;
24912             case "last":
24913                 var total = ds.getTotalCount();
24914                 var extra = total % this.pageSize;
24915                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24916                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24917             break;
24918             case "refresh":
24919                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24920             break;
24921         }
24922     },
24923
24924     /**
24925      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24926      * @param {Roo.data.Store} store The data store to unbind
24927      */
24928     unbind : function(ds){
24929         ds.un("beforeload", this.beforeLoad, this);
24930         ds.un("load", this.onLoad, this);
24931         ds.un("loadexception", this.onLoadError, this);
24932         ds.un("remove", this.updateInfo, this);
24933         ds.un("add", this.updateInfo, this);
24934         this.ds = undefined;
24935     },
24936
24937     /**
24938      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24939      * @param {Roo.data.Store} store The data store to bind
24940      */
24941     bind : function(ds){
24942         ds.on("beforeload", this.beforeLoad, this);
24943         ds.on("load", this.onLoad, this);
24944         ds.on("loadexception", this.onLoadError, this);
24945         ds.on("remove", this.updateInfo, this);
24946         ds.on("add", this.updateInfo, this);
24947         this.ds = ds;
24948     }
24949 });/*
24950  * - LGPL
24951  *
24952  * element
24953  * 
24954  */
24955
24956 /**
24957  * @class Roo.bootstrap.MessageBar
24958  * @extends Roo.bootstrap.Component
24959  * Bootstrap MessageBar class
24960  * @cfg {String} html contents of the MessageBar
24961  * @cfg {String} weight (info | success | warning | danger) default info
24962  * @cfg {String} beforeClass insert the bar before the given class
24963  * @cfg {Boolean} closable (true | false) default false
24964  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24965  * 
24966  * @constructor
24967  * Create a new Element
24968  * @param {Object} config The config object
24969  */
24970
24971 Roo.bootstrap.MessageBar = function(config){
24972     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24973 };
24974
24975 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24976     
24977     html: '',
24978     weight: 'info',
24979     closable: false,
24980     fixed: false,
24981     beforeClass: 'bootstrap-sticky-wrap',
24982     
24983     getAutoCreate : function(){
24984         
24985         var cfg = {
24986             tag: 'div',
24987             cls: 'alert alert-dismissable alert-' + this.weight,
24988             cn: [
24989                 {
24990                     tag: 'span',
24991                     cls: 'message',
24992                     html: this.html || ''
24993                 }
24994             ]
24995         };
24996         
24997         if(this.fixed){
24998             cfg.cls += ' alert-messages-fixed';
24999         }
25000         
25001         if(this.closable){
25002             cfg.cn.push({
25003                 tag: 'button',
25004                 cls: 'close',
25005                 html: 'x'
25006             });
25007         }
25008         
25009         return cfg;
25010     },
25011     
25012     onRender : function(ct, position)
25013     {
25014         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25015         
25016         if(!this.el){
25017             var cfg = Roo.apply({},  this.getAutoCreate());
25018             cfg.id = Roo.id();
25019             
25020             if (this.cls) {
25021                 cfg.cls += ' ' + this.cls;
25022             }
25023             if (this.style) {
25024                 cfg.style = this.style;
25025             }
25026             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25027             
25028             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25029         }
25030         
25031         this.el.select('>button.close').on('click', this.hide, this);
25032         
25033     },
25034     
25035     show : function()
25036     {
25037         if (!this.rendered) {
25038             this.render();
25039         }
25040         
25041         this.el.show();
25042         
25043         this.fireEvent('show', this);
25044         
25045     },
25046     
25047     hide : function()
25048     {
25049         if (!this.rendered) {
25050             this.render();
25051         }
25052         
25053         this.el.hide();
25054         
25055         this.fireEvent('hide', this);
25056     },
25057     
25058     update : function()
25059     {
25060 //        var e = this.el.dom.firstChild;
25061 //        
25062 //        if(this.closable){
25063 //            e = e.nextSibling;
25064 //        }
25065 //        
25066 //        e.data = this.html || '';
25067
25068         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25069     }
25070    
25071 });
25072
25073  
25074
25075      /*
25076  * - LGPL
25077  *
25078  * Graph
25079  * 
25080  */
25081
25082
25083 /**
25084  * @class Roo.bootstrap.Graph
25085  * @extends Roo.bootstrap.Component
25086  * Bootstrap Graph class
25087 > Prameters
25088  -sm {number} sm 4
25089  -md {number} md 5
25090  @cfg {String} graphtype  bar | vbar | pie
25091  @cfg {number} g_x coodinator | centre x (pie)
25092  @cfg {number} g_y coodinator | centre y (pie)
25093  @cfg {number} g_r radius (pie)
25094  @cfg {number} g_height height of the chart (respected by all elements in the set)
25095  @cfg {number} g_width width of the chart (respected by all elements in the set)
25096  @cfg {Object} title The title of the chart
25097     
25098  -{Array}  values
25099  -opts (object) options for the chart 
25100      o {
25101      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25102      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25103      o vgutter (number)
25104      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.
25105      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25106      o to
25107      o stretch (boolean)
25108      o }
25109  -opts (object) options for the pie
25110      o{
25111      o cut
25112      o startAngle (number)
25113      o endAngle (number)
25114      } 
25115  *
25116  * @constructor
25117  * Create a new Input
25118  * @param {Object} config The config object
25119  */
25120
25121 Roo.bootstrap.Graph = function(config){
25122     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25123     
25124     this.addEvents({
25125         // img events
25126         /**
25127          * @event click
25128          * The img click event for the img.
25129          * @param {Roo.EventObject} e
25130          */
25131         "click" : true
25132     });
25133 };
25134
25135 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25136     
25137     sm: 4,
25138     md: 5,
25139     graphtype: 'bar',
25140     g_height: 250,
25141     g_width: 400,
25142     g_x: 50,
25143     g_y: 50,
25144     g_r: 30,
25145     opts:{
25146         //g_colors: this.colors,
25147         g_type: 'soft',
25148         g_gutter: '20%'
25149
25150     },
25151     title : false,
25152
25153     getAutoCreate : function(){
25154         
25155         var cfg = {
25156             tag: 'div',
25157             html : null
25158         };
25159         
25160         
25161         return  cfg;
25162     },
25163
25164     onRender : function(ct,position){
25165         
25166         
25167         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25168         
25169         if (typeof(Raphael) == 'undefined') {
25170             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25171             return;
25172         }
25173         
25174         this.raphael = Raphael(this.el.dom);
25175         
25176                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25177                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25178                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25179                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25180                 /*
25181                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25182                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25183                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25184                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25185                 
25186                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25187                 r.barchart(330, 10, 300, 220, data1);
25188                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25189                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25190                 */
25191                 
25192                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25193                 // r.barchart(30, 30, 560, 250,  xdata, {
25194                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25195                 //     axis : "0 0 1 1",
25196                 //     axisxlabels :  xdata
25197                 //     //yvalues : cols,
25198                    
25199                 // });
25200 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25201 //        
25202 //        this.load(null,xdata,{
25203 //                axis : "0 0 1 1",
25204 //                axisxlabels :  xdata
25205 //                });
25206
25207     },
25208
25209     load : function(graphtype,xdata,opts)
25210     {
25211         this.raphael.clear();
25212         if(!graphtype) {
25213             graphtype = this.graphtype;
25214         }
25215         if(!opts){
25216             opts = this.opts;
25217         }
25218         var r = this.raphael,
25219             fin = function () {
25220                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25221             },
25222             fout = function () {
25223                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25224             },
25225             pfin = function() {
25226                 this.sector.stop();
25227                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25228
25229                 if (this.label) {
25230                     this.label[0].stop();
25231                     this.label[0].attr({ r: 7.5 });
25232                     this.label[1].attr({ "font-weight": 800 });
25233                 }
25234             },
25235             pfout = function() {
25236                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25237
25238                 if (this.label) {
25239                     this.label[0].animate({ r: 5 }, 500, "bounce");
25240                     this.label[1].attr({ "font-weight": 400 });
25241                 }
25242             };
25243
25244         switch(graphtype){
25245             case 'bar':
25246                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25247                 break;
25248             case 'hbar':
25249                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25250                 break;
25251             case 'pie':
25252 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25253 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25254 //            
25255                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25256                 
25257                 break;
25258
25259         }
25260         
25261         if(this.title){
25262             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25263         }
25264         
25265     },
25266     
25267     setTitle: function(o)
25268     {
25269         this.title = o;
25270     },
25271     
25272     initEvents: function() {
25273         
25274         if(!this.href){
25275             this.el.on('click', this.onClick, this);
25276         }
25277     },
25278     
25279     onClick : function(e)
25280     {
25281         Roo.log('img onclick');
25282         this.fireEvent('click', this, e);
25283     }
25284    
25285 });
25286
25287  
25288 /*
25289  * - LGPL
25290  *
25291  * numberBox
25292  * 
25293  */
25294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25295
25296 /**
25297  * @class Roo.bootstrap.dash.NumberBox
25298  * @extends Roo.bootstrap.Component
25299  * Bootstrap NumberBox class
25300  * @cfg {String} headline Box headline
25301  * @cfg {String} content Box content
25302  * @cfg {String} icon Box icon
25303  * @cfg {String} footer Footer text
25304  * @cfg {String} fhref Footer href
25305  * 
25306  * @constructor
25307  * Create a new NumberBox
25308  * @param {Object} config The config object
25309  */
25310
25311
25312 Roo.bootstrap.dash.NumberBox = function(config){
25313     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25314     
25315 };
25316
25317 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25318     
25319     headline : '',
25320     content : '',
25321     icon : '',
25322     footer : '',
25323     fhref : '',
25324     ficon : '',
25325     
25326     getAutoCreate : function(){
25327         
25328         var cfg = {
25329             tag : 'div',
25330             cls : 'small-box ',
25331             cn : [
25332                 {
25333                     tag : 'div',
25334                     cls : 'inner',
25335                     cn :[
25336                         {
25337                             tag : 'h3',
25338                             cls : 'roo-headline',
25339                             html : this.headline
25340                         },
25341                         {
25342                             tag : 'p',
25343                             cls : 'roo-content',
25344                             html : this.content
25345                         }
25346                     ]
25347                 }
25348             ]
25349         };
25350         
25351         if(this.icon){
25352             cfg.cn.push({
25353                 tag : 'div',
25354                 cls : 'icon',
25355                 cn :[
25356                     {
25357                         tag : 'i',
25358                         cls : 'ion ' + this.icon
25359                     }
25360                 ]
25361             });
25362         }
25363         
25364         if(this.footer){
25365             var footer = {
25366                 tag : 'a',
25367                 cls : 'small-box-footer',
25368                 href : this.fhref || '#',
25369                 html : this.footer
25370             };
25371             
25372             cfg.cn.push(footer);
25373             
25374         }
25375         
25376         return  cfg;
25377     },
25378
25379     onRender : function(ct,position){
25380         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25381
25382
25383        
25384                 
25385     },
25386
25387     setHeadline: function (value)
25388     {
25389         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25390     },
25391     
25392     setFooter: function (value, href)
25393     {
25394         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25395         
25396         if(href){
25397             this.el.select('a.small-box-footer',true).first().attr('href', href);
25398         }
25399         
25400     },
25401
25402     setContent: function (value)
25403     {
25404         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25405     },
25406
25407     initEvents: function() 
25408     {   
25409         
25410     }
25411     
25412 });
25413
25414  
25415 /*
25416  * - LGPL
25417  *
25418  * TabBox
25419  * 
25420  */
25421 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25422
25423 /**
25424  * @class Roo.bootstrap.dash.TabBox
25425  * @extends Roo.bootstrap.Component
25426  * Bootstrap TabBox class
25427  * @cfg {String} title Title of the TabBox
25428  * @cfg {String} icon Icon of the TabBox
25429  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25430  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25431  * 
25432  * @constructor
25433  * Create a new TabBox
25434  * @param {Object} config The config object
25435  */
25436
25437
25438 Roo.bootstrap.dash.TabBox = function(config){
25439     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25440     this.addEvents({
25441         // raw events
25442         /**
25443          * @event addpane
25444          * When a pane is added
25445          * @param {Roo.bootstrap.dash.TabPane} pane
25446          */
25447         "addpane" : true,
25448         /**
25449          * @event activatepane
25450          * When a pane is activated
25451          * @param {Roo.bootstrap.dash.TabPane} pane
25452          */
25453         "activatepane" : true
25454         
25455          
25456     });
25457     
25458     this.panes = [];
25459 };
25460
25461 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25462
25463     title : '',
25464     icon : false,
25465     showtabs : true,
25466     tabScrollable : false,
25467     
25468     getChildContainer : function()
25469     {
25470         return this.el.select('.tab-content', true).first();
25471     },
25472     
25473     getAutoCreate : function(){
25474         
25475         var header = {
25476             tag: 'li',
25477             cls: 'pull-left header',
25478             html: this.title,
25479             cn : []
25480         };
25481         
25482         if(this.icon){
25483             header.cn.push({
25484                 tag: 'i',
25485                 cls: 'fa ' + this.icon
25486             });
25487         }
25488         
25489         var h = {
25490             tag: 'ul',
25491             cls: 'nav nav-tabs pull-right',
25492             cn: [
25493                 header
25494             ]
25495         };
25496         
25497         if(this.tabScrollable){
25498             h = {
25499                 tag: 'div',
25500                 cls: 'tab-header',
25501                 cn: [
25502                     {
25503                         tag: 'ul',
25504                         cls: 'nav nav-tabs pull-right',
25505                         cn: [
25506                             header
25507                         ]
25508                     }
25509                 ]
25510             };
25511         }
25512         
25513         var cfg = {
25514             tag: 'div',
25515             cls: 'nav-tabs-custom',
25516             cn: [
25517                 h,
25518                 {
25519                     tag: 'div',
25520                     cls: 'tab-content no-padding',
25521                     cn: []
25522                 }
25523             ]
25524         };
25525
25526         return  cfg;
25527     },
25528     initEvents : function()
25529     {
25530         //Roo.log('add add pane handler');
25531         this.on('addpane', this.onAddPane, this);
25532     },
25533      /**
25534      * Updates the box title
25535      * @param {String} html to set the title to.
25536      */
25537     setTitle : function(value)
25538     {
25539         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25540     },
25541     onAddPane : function(pane)
25542     {
25543         this.panes.push(pane);
25544         //Roo.log('addpane');
25545         //Roo.log(pane);
25546         // tabs are rendere left to right..
25547         if(!this.showtabs){
25548             return;
25549         }
25550         
25551         var ctr = this.el.select('.nav-tabs', true).first();
25552          
25553          
25554         var existing = ctr.select('.nav-tab',true);
25555         var qty = existing.getCount();;
25556         
25557         
25558         var tab = ctr.createChild({
25559             tag : 'li',
25560             cls : 'nav-tab' + (qty ? '' : ' active'),
25561             cn : [
25562                 {
25563                     tag : 'a',
25564                     href:'#',
25565                     html : pane.title
25566                 }
25567             ]
25568         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25569         pane.tab = tab;
25570         
25571         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25572         if (!qty) {
25573             pane.el.addClass('active');
25574         }
25575         
25576                 
25577     },
25578     onTabClick : function(ev,un,ob,pane)
25579     {
25580         //Roo.log('tab - prev default');
25581         ev.preventDefault();
25582         
25583         
25584         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25585         pane.tab.addClass('active');
25586         //Roo.log(pane.title);
25587         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25588         // technically we should have a deactivate event.. but maybe add later.
25589         // and it should not de-activate the selected tab...
25590         this.fireEvent('activatepane', pane);
25591         pane.el.addClass('active');
25592         pane.fireEvent('activate');
25593         
25594         
25595     },
25596     
25597     getActivePane : function()
25598     {
25599         var r = false;
25600         Roo.each(this.panes, function(p) {
25601             if(p.el.hasClass('active')){
25602                 r = p;
25603                 return false;
25604             }
25605             
25606             return;
25607         });
25608         
25609         return r;
25610     }
25611     
25612     
25613 });
25614
25615  
25616 /*
25617  * - LGPL
25618  *
25619  * Tab pane
25620  * 
25621  */
25622 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25623 /**
25624  * @class Roo.bootstrap.TabPane
25625  * @extends Roo.bootstrap.Component
25626  * Bootstrap TabPane class
25627  * @cfg {Boolean} active (false | true) Default false
25628  * @cfg {String} title title of panel
25629
25630  * 
25631  * @constructor
25632  * Create a new TabPane
25633  * @param {Object} config The config object
25634  */
25635
25636 Roo.bootstrap.dash.TabPane = function(config){
25637     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25638     
25639     this.addEvents({
25640         // raw events
25641         /**
25642          * @event activate
25643          * When a pane is activated
25644          * @param {Roo.bootstrap.dash.TabPane} pane
25645          */
25646         "activate" : true
25647          
25648     });
25649 };
25650
25651 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25652     
25653     active : false,
25654     title : '',
25655     
25656     // the tabBox that this is attached to.
25657     tab : false,
25658      
25659     getAutoCreate : function() 
25660     {
25661         var cfg = {
25662             tag: 'div',
25663             cls: 'tab-pane'
25664         };
25665         
25666         if(this.active){
25667             cfg.cls += ' active';
25668         }
25669         
25670         return cfg;
25671     },
25672     initEvents  : function()
25673     {
25674         //Roo.log('trigger add pane handler');
25675         this.parent().fireEvent('addpane', this)
25676     },
25677     
25678      /**
25679      * Updates the tab title 
25680      * @param {String} html to set the title to.
25681      */
25682     setTitle: function(str)
25683     {
25684         if (!this.tab) {
25685             return;
25686         }
25687         this.title = str;
25688         this.tab.select('a', true).first().dom.innerHTML = str;
25689         
25690     }
25691     
25692     
25693     
25694 });
25695
25696  
25697
25698
25699  /*
25700  * - LGPL
25701  *
25702  * menu
25703  * 
25704  */
25705 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25706
25707 /**
25708  * @class Roo.bootstrap.menu.Menu
25709  * @extends Roo.bootstrap.Component
25710  * Bootstrap Menu class - container for Menu
25711  * @cfg {String} html Text of the menu
25712  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25713  * @cfg {String} icon Font awesome icon
25714  * @cfg {String} pos Menu align to (top | bottom) default bottom
25715  * 
25716  * 
25717  * @constructor
25718  * Create a new Menu
25719  * @param {Object} config The config object
25720  */
25721
25722
25723 Roo.bootstrap.menu.Menu = function(config){
25724     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25725     
25726     this.addEvents({
25727         /**
25728          * @event beforeshow
25729          * Fires before this menu is displayed
25730          * @param {Roo.bootstrap.menu.Menu} this
25731          */
25732         beforeshow : true,
25733         /**
25734          * @event beforehide
25735          * Fires before this menu is hidden
25736          * @param {Roo.bootstrap.menu.Menu} this
25737          */
25738         beforehide : true,
25739         /**
25740          * @event show
25741          * Fires after this menu is displayed
25742          * @param {Roo.bootstrap.menu.Menu} this
25743          */
25744         show : true,
25745         /**
25746          * @event hide
25747          * Fires after this menu is hidden
25748          * @param {Roo.bootstrap.menu.Menu} this
25749          */
25750         hide : true,
25751         /**
25752          * @event click
25753          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25754          * @param {Roo.bootstrap.menu.Menu} this
25755          * @param {Roo.EventObject} e
25756          */
25757         click : true
25758     });
25759     
25760 };
25761
25762 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25763     
25764     submenu : false,
25765     html : '',
25766     weight : 'default',
25767     icon : false,
25768     pos : 'bottom',
25769     
25770     
25771     getChildContainer : function() {
25772         if(this.isSubMenu){
25773             return this.el;
25774         }
25775         
25776         return this.el.select('ul.dropdown-menu', true).first();  
25777     },
25778     
25779     getAutoCreate : function()
25780     {
25781         var text = [
25782             {
25783                 tag : 'span',
25784                 cls : 'roo-menu-text',
25785                 html : this.html
25786             }
25787         ];
25788         
25789         if(this.icon){
25790             text.unshift({
25791                 tag : 'i',
25792                 cls : 'fa ' + this.icon
25793             })
25794         }
25795         
25796         
25797         var cfg = {
25798             tag : 'div',
25799             cls : 'btn-group',
25800             cn : [
25801                 {
25802                     tag : 'button',
25803                     cls : 'dropdown-button btn btn-' + this.weight,
25804                     cn : text
25805                 },
25806                 {
25807                     tag : 'button',
25808                     cls : 'dropdown-toggle btn btn-' + this.weight,
25809                     cn : [
25810                         {
25811                             tag : 'span',
25812                             cls : 'caret'
25813                         }
25814                     ]
25815                 },
25816                 {
25817                     tag : 'ul',
25818                     cls : 'dropdown-menu'
25819                 }
25820             ]
25821             
25822         };
25823         
25824         if(this.pos == 'top'){
25825             cfg.cls += ' dropup';
25826         }
25827         
25828         if(this.isSubMenu){
25829             cfg = {
25830                 tag : 'ul',
25831                 cls : 'dropdown-menu'
25832             }
25833         }
25834         
25835         return cfg;
25836     },
25837     
25838     onRender : function(ct, position)
25839     {
25840         this.isSubMenu = ct.hasClass('dropdown-submenu');
25841         
25842         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25843     },
25844     
25845     initEvents : function() 
25846     {
25847         if(this.isSubMenu){
25848             return;
25849         }
25850         
25851         this.hidden = true;
25852         
25853         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25854         this.triggerEl.on('click', this.onTriggerPress, this);
25855         
25856         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25857         this.buttonEl.on('click', this.onClick, this);
25858         
25859     },
25860     
25861     list : function()
25862     {
25863         if(this.isSubMenu){
25864             return this.el;
25865         }
25866         
25867         return this.el.select('ul.dropdown-menu', true).first();
25868     },
25869     
25870     onClick : function(e)
25871     {
25872         this.fireEvent("click", this, e);
25873     },
25874     
25875     onTriggerPress  : function(e)
25876     {   
25877         if (this.isVisible()) {
25878             this.hide();
25879         } else {
25880             this.show();
25881         }
25882     },
25883     
25884     isVisible : function(){
25885         return !this.hidden;
25886     },
25887     
25888     show : function()
25889     {
25890         this.fireEvent("beforeshow", this);
25891         
25892         this.hidden = false;
25893         this.el.addClass('open');
25894         
25895         Roo.get(document).on("mouseup", this.onMouseUp, this);
25896         
25897         this.fireEvent("show", this);
25898         
25899         
25900     },
25901     
25902     hide : function()
25903     {
25904         this.fireEvent("beforehide", this);
25905         
25906         this.hidden = true;
25907         this.el.removeClass('open');
25908         
25909         Roo.get(document).un("mouseup", this.onMouseUp);
25910         
25911         this.fireEvent("hide", this);
25912     },
25913     
25914     onMouseUp : function()
25915     {
25916         this.hide();
25917     }
25918     
25919 });
25920
25921  
25922  /*
25923  * - LGPL
25924  *
25925  * menu item
25926  * 
25927  */
25928 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25929
25930 /**
25931  * @class Roo.bootstrap.menu.Item
25932  * @extends Roo.bootstrap.Component
25933  * Bootstrap MenuItem class
25934  * @cfg {Boolean} submenu (true | false) default false
25935  * @cfg {String} html text of the item
25936  * @cfg {String} href the link
25937  * @cfg {Boolean} disable (true | false) default false
25938  * @cfg {Boolean} preventDefault (true | false) default true
25939  * @cfg {String} icon Font awesome icon
25940  * @cfg {String} pos Submenu align to (left | right) default right 
25941  * 
25942  * 
25943  * @constructor
25944  * Create a new Item
25945  * @param {Object} config The config object
25946  */
25947
25948
25949 Roo.bootstrap.menu.Item = function(config){
25950     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25951     this.addEvents({
25952         /**
25953          * @event mouseover
25954          * Fires when the mouse is hovering over this menu
25955          * @param {Roo.bootstrap.menu.Item} this
25956          * @param {Roo.EventObject} e
25957          */
25958         mouseover : true,
25959         /**
25960          * @event mouseout
25961          * Fires when the mouse exits this menu
25962          * @param {Roo.bootstrap.menu.Item} this
25963          * @param {Roo.EventObject} e
25964          */
25965         mouseout : true,
25966         // raw events
25967         /**
25968          * @event click
25969          * The raw click event for the entire grid.
25970          * @param {Roo.EventObject} e
25971          */
25972         click : true
25973     });
25974 };
25975
25976 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25977     
25978     submenu : false,
25979     href : '',
25980     html : '',
25981     preventDefault: true,
25982     disable : false,
25983     icon : false,
25984     pos : 'right',
25985     
25986     getAutoCreate : function()
25987     {
25988         var text = [
25989             {
25990                 tag : 'span',
25991                 cls : 'roo-menu-item-text',
25992                 html : this.html
25993             }
25994         ];
25995         
25996         if(this.icon){
25997             text.unshift({
25998                 tag : 'i',
25999                 cls : 'fa ' + this.icon
26000             })
26001         }
26002         
26003         var cfg = {
26004             tag : 'li',
26005             cn : [
26006                 {
26007                     tag : 'a',
26008                     href : this.href || '#',
26009                     cn : text
26010                 }
26011             ]
26012         };
26013         
26014         if(this.disable){
26015             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26016         }
26017         
26018         if(this.submenu){
26019             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26020             
26021             if(this.pos == 'left'){
26022                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26023             }
26024         }
26025         
26026         return cfg;
26027     },
26028     
26029     initEvents : function() 
26030     {
26031         this.el.on('mouseover', this.onMouseOver, this);
26032         this.el.on('mouseout', this.onMouseOut, this);
26033         
26034         this.el.select('a', true).first().on('click', this.onClick, this);
26035         
26036     },
26037     
26038     onClick : function(e)
26039     {
26040         if(this.preventDefault){
26041             e.preventDefault();
26042         }
26043         
26044         this.fireEvent("click", this, e);
26045     },
26046     
26047     onMouseOver : function(e)
26048     {
26049         if(this.submenu && this.pos == 'left'){
26050             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26051         }
26052         
26053         this.fireEvent("mouseover", this, e);
26054     },
26055     
26056     onMouseOut : function(e)
26057     {
26058         this.fireEvent("mouseout", this, e);
26059     }
26060 });
26061
26062  
26063
26064  /*
26065  * - LGPL
26066  *
26067  * menu separator
26068  * 
26069  */
26070 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26071
26072 /**
26073  * @class Roo.bootstrap.menu.Separator
26074  * @extends Roo.bootstrap.Component
26075  * Bootstrap Separator class
26076  * 
26077  * @constructor
26078  * Create a new Separator
26079  * @param {Object} config The config object
26080  */
26081
26082
26083 Roo.bootstrap.menu.Separator = function(config){
26084     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26085 };
26086
26087 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26088     
26089     getAutoCreate : function(){
26090         var cfg = {
26091             tag : 'li',
26092             cls: 'divider'
26093         };
26094         
26095         return cfg;
26096     }
26097    
26098 });
26099
26100  
26101
26102  /*
26103  * - LGPL
26104  *
26105  * Tooltip
26106  * 
26107  */
26108
26109 /**
26110  * @class Roo.bootstrap.Tooltip
26111  * Bootstrap Tooltip class
26112  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26113  * to determine which dom element triggers the tooltip.
26114  * 
26115  * It needs to add support for additional attributes like tooltip-position
26116  * 
26117  * @constructor
26118  * Create a new Toolti
26119  * @param {Object} config The config object
26120  */
26121
26122 Roo.bootstrap.Tooltip = function(config){
26123     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26124     
26125     this.alignment = Roo.bootstrap.Tooltip.alignment;
26126     
26127     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26128         this.alignment = config.alignment;
26129     }
26130     
26131 };
26132
26133 Roo.apply(Roo.bootstrap.Tooltip, {
26134     /**
26135      * @function init initialize tooltip monitoring.
26136      * @static
26137      */
26138     currentEl : false,
26139     currentTip : false,
26140     currentRegion : false,
26141     
26142     //  init : delay?
26143     
26144     init : function()
26145     {
26146         Roo.get(document).on('mouseover', this.enter ,this);
26147         Roo.get(document).on('mouseout', this.leave, this);
26148          
26149         
26150         this.currentTip = new Roo.bootstrap.Tooltip();
26151     },
26152     
26153     enter : function(ev)
26154     {
26155         var dom = ev.getTarget();
26156         
26157         //Roo.log(['enter',dom]);
26158         var el = Roo.fly(dom);
26159         if (this.currentEl) {
26160             //Roo.log(dom);
26161             //Roo.log(this.currentEl);
26162             //Roo.log(this.currentEl.contains(dom));
26163             if (this.currentEl == el) {
26164                 return;
26165             }
26166             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26167                 return;
26168             }
26169
26170         }
26171         
26172         if (this.currentTip.el) {
26173             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26174         }    
26175         //Roo.log(ev);
26176         
26177         if(!el || el.dom == document){
26178             return;
26179         }
26180         
26181         var bindEl = el;
26182         
26183         // you can not look for children, as if el is the body.. then everythign is the child..
26184         if (!el.attr('tooltip')) { //
26185             if (!el.select("[tooltip]").elements.length) {
26186                 return;
26187             }
26188             // is the mouse over this child...?
26189             bindEl = el.select("[tooltip]").first();
26190             var xy = ev.getXY();
26191             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26192                 //Roo.log("not in region.");
26193                 return;
26194             }
26195             //Roo.log("child element over..");
26196             
26197         }
26198         this.currentEl = bindEl;
26199         this.currentTip.bind(bindEl);
26200         this.currentRegion = Roo.lib.Region.getRegion(dom);
26201         this.currentTip.enter();
26202         
26203     },
26204     leave : function(ev)
26205     {
26206         var dom = ev.getTarget();
26207         //Roo.log(['leave',dom]);
26208         if (!this.currentEl) {
26209             return;
26210         }
26211         
26212         
26213         if (dom != this.currentEl.dom) {
26214             return;
26215         }
26216         var xy = ev.getXY();
26217         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26218             return;
26219         }
26220         // only activate leave if mouse cursor is outside... bounding box..
26221         
26222         
26223         
26224         
26225         if (this.currentTip) {
26226             this.currentTip.leave();
26227         }
26228         //Roo.log('clear currentEl');
26229         this.currentEl = false;
26230         
26231         
26232     },
26233     alignment : {
26234         'left' : ['r-l', [-2,0], 'right'],
26235         'right' : ['l-r', [2,0], 'left'],
26236         'bottom' : ['t-b', [0,2], 'top'],
26237         'top' : [ 'b-t', [0,-2], 'bottom']
26238     }
26239     
26240 });
26241
26242
26243 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26244     
26245     
26246     bindEl : false,
26247     
26248     delay : null, // can be { show : 300 , hide: 500}
26249     
26250     timeout : null,
26251     
26252     hoverState : null, //???
26253     
26254     placement : 'bottom', 
26255     
26256     alignment : false,
26257     
26258     getAutoCreate : function(){
26259     
26260         var cfg = {
26261            cls : 'tooltip',
26262            role : 'tooltip',
26263            cn : [
26264                 {
26265                     cls : 'tooltip-arrow'
26266                 },
26267                 {
26268                     cls : 'tooltip-inner'
26269                 }
26270            ]
26271         };
26272         
26273         return cfg;
26274     },
26275     bind : function(el)
26276     {
26277         this.bindEl = el;
26278     },
26279       
26280     
26281     enter : function () {
26282        
26283         if (this.timeout != null) {
26284             clearTimeout(this.timeout);
26285         }
26286         
26287         this.hoverState = 'in';
26288          //Roo.log("enter - show");
26289         if (!this.delay || !this.delay.show) {
26290             this.show();
26291             return;
26292         }
26293         var _t = this;
26294         this.timeout = setTimeout(function () {
26295             if (_t.hoverState == 'in') {
26296                 _t.show();
26297             }
26298         }, this.delay.show);
26299     },
26300     leave : function()
26301     {
26302         clearTimeout(this.timeout);
26303     
26304         this.hoverState = 'out';
26305          if (!this.delay || !this.delay.hide) {
26306             this.hide();
26307             return;
26308         }
26309        
26310         var _t = this;
26311         this.timeout = setTimeout(function () {
26312             //Roo.log("leave - timeout");
26313             
26314             if (_t.hoverState == 'out') {
26315                 _t.hide();
26316                 Roo.bootstrap.Tooltip.currentEl = false;
26317             }
26318         }, delay);
26319     },
26320     
26321     show : function (msg)
26322     {
26323         if (!this.el) {
26324             this.render(document.body);
26325         }
26326         // set content.
26327         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26328         
26329         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26330         
26331         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26332         
26333         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26334         
26335         var placement = typeof this.placement == 'function' ?
26336             this.placement.call(this, this.el, on_el) :
26337             this.placement;
26338             
26339         var autoToken = /\s?auto?\s?/i;
26340         var autoPlace = autoToken.test(placement);
26341         if (autoPlace) {
26342             placement = placement.replace(autoToken, '') || 'top';
26343         }
26344         
26345         //this.el.detach()
26346         //this.el.setXY([0,0]);
26347         this.el.show();
26348         //this.el.dom.style.display='block';
26349         
26350         //this.el.appendTo(on_el);
26351         
26352         var p = this.getPosition();
26353         var box = this.el.getBox();
26354         
26355         if (autoPlace) {
26356             // fixme..
26357         }
26358         
26359         var align = this.alignment[placement];
26360         
26361         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26362         
26363         if(placement == 'top' || placement == 'bottom'){
26364             if(xy[0] < 0){
26365                 placement = 'right';
26366             }
26367             
26368             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26369                 placement = 'left';
26370             }
26371             
26372             var scroll = Roo.select('body', true).first().getScroll();
26373             
26374             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26375                 placement = 'top';
26376             }
26377             
26378             align = this.alignment[placement];
26379         }
26380         
26381         this.el.alignTo(this.bindEl, align[0],align[1]);
26382         //var arrow = this.el.select('.arrow',true).first();
26383         //arrow.set(align[2], 
26384         
26385         this.el.addClass(placement);
26386         
26387         this.el.addClass('in fade');
26388         
26389         this.hoverState = null;
26390         
26391         if (this.el.hasClass('fade')) {
26392             // fade it?
26393         }
26394         
26395     },
26396     hide : function()
26397     {
26398          
26399         if (!this.el) {
26400             return;
26401         }
26402         //this.el.setXY([0,0]);
26403         this.el.removeClass('in');
26404         //this.el.hide();
26405         
26406     }
26407     
26408 });
26409  
26410
26411  /*
26412  * - LGPL
26413  *
26414  * Location Picker
26415  * 
26416  */
26417
26418 /**
26419  * @class Roo.bootstrap.LocationPicker
26420  * @extends Roo.bootstrap.Component
26421  * Bootstrap LocationPicker class
26422  * @cfg {Number} latitude Position when init default 0
26423  * @cfg {Number} longitude Position when init default 0
26424  * @cfg {Number} zoom default 15
26425  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26426  * @cfg {Boolean} mapTypeControl default false
26427  * @cfg {Boolean} disableDoubleClickZoom default false
26428  * @cfg {Boolean} scrollwheel default true
26429  * @cfg {Boolean} streetViewControl default false
26430  * @cfg {Number} radius default 0
26431  * @cfg {String} locationName
26432  * @cfg {Boolean} draggable default true
26433  * @cfg {Boolean} enableAutocomplete default false
26434  * @cfg {Boolean} enableReverseGeocode default true
26435  * @cfg {String} markerTitle
26436  * 
26437  * @constructor
26438  * Create a new LocationPicker
26439  * @param {Object} config The config object
26440  */
26441
26442
26443 Roo.bootstrap.LocationPicker = function(config){
26444     
26445     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26446     
26447     this.addEvents({
26448         /**
26449          * @event initial
26450          * Fires when the picker initialized.
26451          * @param {Roo.bootstrap.LocationPicker} this
26452          * @param {Google Location} location
26453          */
26454         initial : true,
26455         /**
26456          * @event positionchanged
26457          * Fires when the picker position changed.
26458          * @param {Roo.bootstrap.LocationPicker} this
26459          * @param {Google Location} location
26460          */
26461         positionchanged : true,
26462         /**
26463          * @event resize
26464          * Fires when the map resize.
26465          * @param {Roo.bootstrap.LocationPicker} this
26466          */
26467         resize : true,
26468         /**
26469          * @event show
26470          * Fires when the map show.
26471          * @param {Roo.bootstrap.LocationPicker} this
26472          */
26473         show : true,
26474         /**
26475          * @event hide
26476          * Fires when the map hide.
26477          * @param {Roo.bootstrap.LocationPicker} this
26478          */
26479         hide : true,
26480         /**
26481          * @event mapClick
26482          * Fires when click the map.
26483          * @param {Roo.bootstrap.LocationPicker} this
26484          * @param {Map event} e
26485          */
26486         mapClick : true,
26487         /**
26488          * @event mapRightClick
26489          * Fires when right click the map.
26490          * @param {Roo.bootstrap.LocationPicker} this
26491          * @param {Map event} e
26492          */
26493         mapRightClick : true,
26494         /**
26495          * @event markerClick
26496          * Fires when click the marker.
26497          * @param {Roo.bootstrap.LocationPicker} this
26498          * @param {Map event} e
26499          */
26500         markerClick : true,
26501         /**
26502          * @event markerRightClick
26503          * Fires when right click the marker.
26504          * @param {Roo.bootstrap.LocationPicker} this
26505          * @param {Map event} e
26506          */
26507         markerRightClick : true,
26508         /**
26509          * @event OverlayViewDraw
26510          * Fires when OverlayView Draw
26511          * @param {Roo.bootstrap.LocationPicker} this
26512          */
26513         OverlayViewDraw : true,
26514         /**
26515          * @event OverlayViewOnAdd
26516          * Fires when OverlayView Draw
26517          * @param {Roo.bootstrap.LocationPicker} this
26518          */
26519         OverlayViewOnAdd : true,
26520         /**
26521          * @event OverlayViewOnRemove
26522          * Fires when OverlayView Draw
26523          * @param {Roo.bootstrap.LocationPicker} this
26524          */
26525         OverlayViewOnRemove : true,
26526         /**
26527          * @event OverlayViewShow
26528          * Fires when OverlayView Draw
26529          * @param {Roo.bootstrap.LocationPicker} this
26530          * @param {Pixel} cpx
26531          */
26532         OverlayViewShow : true,
26533         /**
26534          * @event OverlayViewHide
26535          * Fires when OverlayView Draw
26536          * @param {Roo.bootstrap.LocationPicker} this
26537          */
26538         OverlayViewHide : true,
26539         /**
26540          * @event loadexception
26541          * Fires when load google lib failed.
26542          * @param {Roo.bootstrap.LocationPicker} this
26543          */
26544         loadexception : true
26545     });
26546         
26547 };
26548
26549 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26550     
26551     gMapContext: false,
26552     
26553     latitude: 0,
26554     longitude: 0,
26555     zoom: 15,
26556     mapTypeId: false,
26557     mapTypeControl: false,
26558     disableDoubleClickZoom: false,
26559     scrollwheel: true,
26560     streetViewControl: false,
26561     radius: 0,
26562     locationName: '',
26563     draggable: true,
26564     enableAutocomplete: false,
26565     enableReverseGeocode: true,
26566     markerTitle: '',
26567     
26568     getAutoCreate: function()
26569     {
26570
26571         var cfg = {
26572             tag: 'div',
26573             cls: 'roo-location-picker'
26574         };
26575         
26576         return cfg
26577     },
26578     
26579     initEvents: function(ct, position)
26580     {       
26581         if(!this.el.getWidth() || this.isApplied()){
26582             return;
26583         }
26584         
26585         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26586         
26587         this.initial();
26588     },
26589     
26590     initial: function()
26591     {
26592         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26593             this.fireEvent('loadexception', this);
26594             return;
26595         }
26596         
26597         if(!this.mapTypeId){
26598             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26599         }
26600         
26601         this.gMapContext = this.GMapContext();
26602         
26603         this.initOverlayView();
26604         
26605         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26606         
26607         var _this = this;
26608                 
26609         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26610             _this.setPosition(_this.gMapContext.marker.position);
26611         });
26612         
26613         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26614             _this.fireEvent('mapClick', this, event);
26615             
26616         });
26617
26618         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26619             _this.fireEvent('mapRightClick', this, event);
26620             
26621         });
26622         
26623         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26624             _this.fireEvent('markerClick', this, event);
26625             
26626         });
26627
26628         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26629             _this.fireEvent('markerRightClick', this, event);
26630             
26631         });
26632         
26633         this.setPosition(this.gMapContext.location);
26634         
26635         this.fireEvent('initial', this, this.gMapContext.location);
26636     },
26637     
26638     initOverlayView: function()
26639     {
26640         var _this = this;
26641         
26642         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26643             
26644             draw: function()
26645             {
26646                 _this.fireEvent('OverlayViewDraw', _this);
26647             },
26648             
26649             onAdd: function()
26650             {
26651                 _this.fireEvent('OverlayViewOnAdd', _this);
26652             },
26653             
26654             onRemove: function()
26655             {
26656                 _this.fireEvent('OverlayViewOnRemove', _this);
26657             },
26658             
26659             show: function(cpx)
26660             {
26661                 _this.fireEvent('OverlayViewShow', _this, cpx);
26662             },
26663             
26664             hide: function()
26665             {
26666                 _this.fireEvent('OverlayViewHide', _this);
26667             }
26668             
26669         });
26670     },
26671     
26672     fromLatLngToContainerPixel: function(event)
26673     {
26674         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26675     },
26676     
26677     isApplied: function() 
26678     {
26679         return this.getGmapContext() == false ? false : true;
26680     },
26681     
26682     getGmapContext: function() 
26683     {
26684         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26685     },
26686     
26687     GMapContext: function() 
26688     {
26689         var position = new google.maps.LatLng(this.latitude, this.longitude);
26690         
26691         var _map = new google.maps.Map(this.el.dom, {
26692             center: position,
26693             zoom: this.zoom,
26694             mapTypeId: this.mapTypeId,
26695             mapTypeControl: this.mapTypeControl,
26696             disableDoubleClickZoom: this.disableDoubleClickZoom,
26697             scrollwheel: this.scrollwheel,
26698             streetViewControl: this.streetViewControl,
26699             locationName: this.locationName,
26700             draggable: this.draggable,
26701             enableAutocomplete: this.enableAutocomplete,
26702             enableReverseGeocode: this.enableReverseGeocode
26703         });
26704         
26705         var _marker = new google.maps.Marker({
26706             position: position,
26707             map: _map,
26708             title: this.markerTitle,
26709             draggable: this.draggable
26710         });
26711         
26712         return {
26713             map: _map,
26714             marker: _marker,
26715             circle: null,
26716             location: position,
26717             radius: this.radius,
26718             locationName: this.locationName,
26719             addressComponents: {
26720                 formatted_address: null,
26721                 addressLine1: null,
26722                 addressLine2: null,
26723                 streetName: null,
26724                 streetNumber: null,
26725                 city: null,
26726                 district: null,
26727                 state: null,
26728                 stateOrProvince: null
26729             },
26730             settings: this,
26731             domContainer: this.el.dom,
26732             geodecoder: new google.maps.Geocoder()
26733         };
26734     },
26735     
26736     drawCircle: function(center, radius, options) 
26737     {
26738         if (this.gMapContext.circle != null) {
26739             this.gMapContext.circle.setMap(null);
26740         }
26741         if (radius > 0) {
26742             radius *= 1;
26743             options = Roo.apply({}, options, {
26744                 strokeColor: "#0000FF",
26745                 strokeOpacity: .35,
26746                 strokeWeight: 2,
26747                 fillColor: "#0000FF",
26748                 fillOpacity: .2
26749             });
26750             
26751             options.map = this.gMapContext.map;
26752             options.radius = radius;
26753             options.center = center;
26754             this.gMapContext.circle = new google.maps.Circle(options);
26755             return this.gMapContext.circle;
26756         }
26757         
26758         return null;
26759     },
26760     
26761     setPosition: function(location) 
26762     {
26763         this.gMapContext.location = location;
26764         this.gMapContext.marker.setPosition(location);
26765         this.gMapContext.map.panTo(location);
26766         this.drawCircle(location, this.gMapContext.radius, {});
26767         
26768         var _this = this;
26769         
26770         if (this.gMapContext.settings.enableReverseGeocode) {
26771             this.gMapContext.geodecoder.geocode({
26772                 latLng: this.gMapContext.location
26773             }, function(results, status) {
26774                 
26775                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26776                     _this.gMapContext.locationName = results[0].formatted_address;
26777                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26778                     
26779                     _this.fireEvent('positionchanged', this, location);
26780                 }
26781             });
26782             
26783             return;
26784         }
26785         
26786         this.fireEvent('positionchanged', this, location);
26787     },
26788     
26789     resize: function()
26790     {
26791         google.maps.event.trigger(this.gMapContext.map, "resize");
26792         
26793         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26794         
26795         this.fireEvent('resize', this);
26796     },
26797     
26798     setPositionByLatLng: function(latitude, longitude)
26799     {
26800         this.setPosition(new google.maps.LatLng(latitude, longitude));
26801     },
26802     
26803     getCurrentPosition: function() 
26804     {
26805         return {
26806             latitude: this.gMapContext.location.lat(),
26807             longitude: this.gMapContext.location.lng()
26808         };
26809     },
26810     
26811     getAddressName: function() 
26812     {
26813         return this.gMapContext.locationName;
26814     },
26815     
26816     getAddressComponents: function() 
26817     {
26818         return this.gMapContext.addressComponents;
26819     },
26820     
26821     address_component_from_google_geocode: function(address_components) 
26822     {
26823         var result = {};
26824         
26825         for (var i = 0; i < address_components.length; i++) {
26826             var component = address_components[i];
26827             if (component.types.indexOf("postal_code") >= 0) {
26828                 result.postalCode = component.short_name;
26829             } else if (component.types.indexOf("street_number") >= 0) {
26830                 result.streetNumber = component.short_name;
26831             } else if (component.types.indexOf("route") >= 0) {
26832                 result.streetName = component.short_name;
26833             } else if (component.types.indexOf("neighborhood") >= 0) {
26834                 result.city = component.short_name;
26835             } else if (component.types.indexOf("locality") >= 0) {
26836                 result.city = component.short_name;
26837             } else if (component.types.indexOf("sublocality") >= 0) {
26838                 result.district = component.short_name;
26839             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26840                 result.stateOrProvince = component.short_name;
26841             } else if (component.types.indexOf("country") >= 0) {
26842                 result.country = component.short_name;
26843             }
26844         }
26845         
26846         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26847         result.addressLine2 = "";
26848         return result;
26849     },
26850     
26851     setZoomLevel: function(zoom)
26852     {
26853         this.gMapContext.map.setZoom(zoom);
26854     },
26855     
26856     show: function()
26857     {
26858         if(!this.el){
26859             return;
26860         }
26861         
26862         this.el.show();
26863         
26864         this.resize();
26865         
26866         this.fireEvent('show', this);
26867     },
26868     
26869     hide: function()
26870     {
26871         if(!this.el){
26872             return;
26873         }
26874         
26875         this.el.hide();
26876         
26877         this.fireEvent('hide', this);
26878     }
26879     
26880 });
26881
26882 Roo.apply(Roo.bootstrap.LocationPicker, {
26883     
26884     OverlayView : function(map, options)
26885     {
26886         options = options || {};
26887         
26888         this.setMap(map);
26889     }
26890     
26891     
26892 });/*
26893  * - LGPL
26894  *
26895  * Alert
26896  * 
26897  */
26898
26899 /**
26900  * @class Roo.bootstrap.Alert
26901  * @extends Roo.bootstrap.Component
26902  * Bootstrap Alert class
26903  * @cfg {String} title The title of alert
26904  * @cfg {String} html The content of alert
26905  * @cfg {String} weight (  success | info | warning | danger )
26906  * @cfg {String} faicon font-awesomeicon
26907  * 
26908  * @constructor
26909  * Create a new alert
26910  * @param {Object} config The config object
26911  */
26912
26913
26914 Roo.bootstrap.Alert = function(config){
26915     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26916     
26917 };
26918
26919 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26920     
26921     title: '',
26922     html: '',
26923     weight: false,
26924     faicon: false,
26925     
26926     getAutoCreate : function()
26927     {
26928         
26929         var cfg = {
26930             tag : 'div',
26931             cls : 'alert',
26932             cn : [
26933                 {
26934                     tag : 'i',
26935                     cls : 'roo-alert-icon'
26936                     
26937                 },
26938                 {
26939                     tag : 'b',
26940                     cls : 'roo-alert-title',
26941                     html : this.title
26942                 },
26943                 {
26944                     tag : 'span',
26945                     cls : 'roo-alert-text',
26946                     html : this.html
26947                 }
26948             ]
26949         };
26950         
26951         if(this.faicon){
26952             cfg.cn[0].cls += ' fa ' + this.faicon;
26953         }
26954         
26955         if(this.weight){
26956             cfg.cls += ' alert-' + this.weight;
26957         }
26958         
26959         return cfg;
26960     },
26961     
26962     initEvents: function() 
26963     {
26964         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26965     },
26966     
26967     setTitle : function(str)
26968     {
26969         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26970     },
26971     
26972     setText : function(str)
26973     {
26974         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26975     },
26976     
26977     setWeight : function(weight)
26978     {
26979         if(this.weight){
26980             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26981         }
26982         
26983         this.weight = weight;
26984         
26985         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26986     },
26987     
26988     setIcon : function(icon)
26989     {
26990         if(this.faicon){
26991             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26992         }
26993         
26994         this.faicon = icon;
26995         
26996         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26997     },
26998     
26999     hide: function() 
27000     {
27001         this.el.hide();   
27002     },
27003     
27004     show: function() 
27005     {  
27006         this.el.show();   
27007     }
27008     
27009 });
27010
27011  
27012 /*
27013 * Licence: LGPL
27014 */
27015
27016 /**
27017  * @class Roo.bootstrap.UploadCropbox
27018  * @extends Roo.bootstrap.Component
27019  * Bootstrap UploadCropbox class
27020  * @cfg {String} emptyText show when image has been loaded
27021  * @cfg {String} rotateNotify show when image too small to rotate
27022  * @cfg {Number} errorTimeout default 3000
27023  * @cfg {Number} minWidth default 300
27024  * @cfg {Number} minHeight default 300
27025  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27026  * @cfg {Boolean} isDocument (true|false) default false
27027  * @cfg {String} url action url
27028  * @cfg {String} paramName default 'imageUpload'
27029  * @cfg {String} method default POST
27030  * @cfg {Boolean} loadMask (true|false) default true
27031  * @cfg {Boolean} loadingText default 'Loading...'
27032  * 
27033  * @constructor
27034  * Create a new UploadCropbox
27035  * @param {Object} config The config object
27036  */
27037
27038 Roo.bootstrap.UploadCropbox = function(config){
27039     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27040     
27041     this.addEvents({
27042         /**
27043          * @event beforeselectfile
27044          * Fire before select file
27045          * @param {Roo.bootstrap.UploadCropbox} this
27046          */
27047         "beforeselectfile" : true,
27048         /**
27049          * @event initial
27050          * Fire after initEvent
27051          * @param {Roo.bootstrap.UploadCropbox} this
27052          */
27053         "initial" : true,
27054         /**
27055          * @event crop
27056          * Fire after initEvent
27057          * @param {Roo.bootstrap.UploadCropbox} this
27058          * @param {String} data
27059          */
27060         "crop" : true,
27061         /**
27062          * @event prepare
27063          * Fire when preparing the file data
27064          * @param {Roo.bootstrap.UploadCropbox} this
27065          * @param {Object} file
27066          */
27067         "prepare" : true,
27068         /**
27069          * @event exception
27070          * Fire when get exception
27071          * @param {Roo.bootstrap.UploadCropbox} this
27072          * @param {XMLHttpRequest} xhr
27073          */
27074         "exception" : true,
27075         /**
27076          * @event beforeloadcanvas
27077          * Fire before load the canvas
27078          * @param {Roo.bootstrap.UploadCropbox} this
27079          * @param {String} src
27080          */
27081         "beforeloadcanvas" : true,
27082         /**
27083          * @event trash
27084          * Fire when trash image
27085          * @param {Roo.bootstrap.UploadCropbox} this
27086          */
27087         "trash" : true,
27088         /**
27089          * @event download
27090          * Fire when download the image
27091          * @param {Roo.bootstrap.UploadCropbox} this
27092          */
27093         "download" : true,
27094         /**
27095          * @event footerbuttonclick
27096          * Fire when footerbuttonclick
27097          * @param {Roo.bootstrap.UploadCropbox} this
27098          * @param {String} type
27099          */
27100         "footerbuttonclick" : true,
27101         /**
27102          * @event resize
27103          * Fire when resize
27104          * @param {Roo.bootstrap.UploadCropbox} this
27105          */
27106         "resize" : true,
27107         /**
27108          * @event rotate
27109          * Fire when rotate the image
27110          * @param {Roo.bootstrap.UploadCropbox} this
27111          * @param {String} pos
27112          */
27113         "rotate" : true,
27114         /**
27115          * @event inspect
27116          * Fire when inspect the file
27117          * @param {Roo.bootstrap.UploadCropbox} this
27118          * @param {Object} file
27119          */
27120         "inspect" : true,
27121         /**
27122          * @event upload
27123          * Fire when xhr upload the file
27124          * @param {Roo.bootstrap.UploadCropbox} this
27125          * @param {Object} data
27126          */
27127         "upload" : true,
27128         /**
27129          * @event arrange
27130          * Fire when arrange the file data
27131          * @param {Roo.bootstrap.UploadCropbox} this
27132          * @param {Object} formData
27133          */
27134         "arrange" : true
27135     });
27136     
27137     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27138 };
27139
27140 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27141     
27142     emptyText : 'Click to upload image',
27143     rotateNotify : 'Image is too small to rotate',
27144     errorTimeout : 3000,
27145     scale : 0,
27146     baseScale : 1,
27147     rotate : 0,
27148     dragable : false,
27149     pinching : false,
27150     mouseX : 0,
27151     mouseY : 0,
27152     cropData : false,
27153     minWidth : 300,
27154     minHeight : 300,
27155     file : false,
27156     exif : {},
27157     baseRotate : 1,
27158     cropType : 'image/jpeg',
27159     buttons : false,
27160     canvasLoaded : false,
27161     isDocument : false,
27162     method : 'POST',
27163     paramName : 'imageUpload',
27164     loadMask : true,
27165     loadingText : 'Loading...',
27166     maskEl : false,
27167     
27168     getAutoCreate : function()
27169     {
27170         var cfg = {
27171             tag : 'div',
27172             cls : 'roo-upload-cropbox',
27173             cn : [
27174                 {
27175                     tag : 'input',
27176                     cls : 'roo-upload-cropbox-selector',
27177                     type : 'file'
27178                 },
27179                 {
27180                     tag : 'div',
27181                     cls : 'roo-upload-cropbox-body',
27182                     style : 'cursor:pointer',
27183                     cn : [
27184                         {
27185                             tag : 'div',
27186                             cls : 'roo-upload-cropbox-preview'
27187                         },
27188                         {
27189                             tag : 'div',
27190                             cls : 'roo-upload-cropbox-thumb'
27191                         },
27192                         {
27193                             tag : 'div',
27194                             cls : 'roo-upload-cropbox-empty-notify',
27195                             html : this.emptyText
27196                         },
27197                         {
27198                             tag : 'div',
27199                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27200                             html : this.rotateNotify
27201                         }
27202                     ]
27203                 },
27204                 {
27205                     tag : 'div',
27206                     cls : 'roo-upload-cropbox-footer',
27207                     cn : {
27208                         tag : 'div',
27209                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27210                         cn : []
27211                     }
27212                 }
27213             ]
27214         };
27215         
27216         return cfg;
27217     },
27218     
27219     onRender : function(ct, position)
27220     {
27221         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27222         
27223         if (this.buttons.length) {
27224             
27225             Roo.each(this.buttons, function(bb) {
27226                 
27227                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27228                 
27229                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27230                 
27231             }, this);
27232         }
27233         
27234         if(this.loadMask){
27235             this.maskEl = this.el;
27236         }
27237     },
27238     
27239     initEvents : function()
27240     {
27241         this.urlAPI = (window.createObjectURL && window) || 
27242                                 (window.URL && URL.revokeObjectURL && URL) || 
27243                                 (window.webkitURL && webkitURL);
27244                         
27245         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27246         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27247         
27248         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27249         this.selectorEl.hide();
27250         
27251         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27252         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27253         
27254         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27255         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27256         this.thumbEl.hide();
27257         
27258         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27259         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27260         
27261         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27262         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27263         this.errorEl.hide();
27264         
27265         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27266         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27267         this.footerEl.hide();
27268         
27269         this.setThumbBoxSize();
27270         
27271         this.bind();
27272         
27273         this.resize();
27274         
27275         this.fireEvent('initial', this);
27276     },
27277
27278     bind : function()
27279     {
27280         var _this = this;
27281         
27282         window.addEventListener("resize", function() { _this.resize(); } );
27283         
27284         this.bodyEl.on('click', this.beforeSelectFile, this);
27285         
27286         if(Roo.isTouch){
27287             this.bodyEl.on('touchstart', this.onTouchStart, this);
27288             this.bodyEl.on('touchmove', this.onTouchMove, this);
27289             this.bodyEl.on('touchend', this.onTouchEnd, this);
27290         }
27291         
27292         if(!Roo.isTouch){
27293             this.bodyEl.on('mousedown', this.onMouseDown, this);
27294             this.bodyEl.on('mousemove', this.onMouseMove, this);
27295             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27296             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27297             Roo.get(document).on('mouseup', this.onMouseUp, this);
27298         }
27299         
27300         this.selectorEl.on('change', this.onFileSelected, this);
27301     },
27302     
27303     reset : function()
27304     {    
27305         this.scale = 0;
27306         this.baseScale = 1;
27307         this.rotate = 0;
27308         this.baseRotate = 1;
27309         this.dragable = false;
27310         this.pinching = false;
27311         this.mouseX = 0;
27312         this.mouseY = 0;
27313         this.cropData = false;
27314         this.notifyEl.dom.innerHTML = this.emptyText;
27315         
27316         this.selectorEl.dom.value = '';
27317         
27318     },
27319     
27320     resize : function()
27321     {
27322         if(this.fireEvent('resize', this) != false){
27323             this.setThumbBoxPosition();
27324             this.setCanvasPosition();
27325         }
27326     },
27327     
27328     onFooterButtonClick : function(e, el, o, type)
27329     {
27330         switch (type) {
27331             case 'rotate-left' :
27332                 this.onRotateLeft(e);
27333                 break;
27334             case 'rotate-right' :
27335                 this.onRotateRight(e);
27336                 break;
27337             case 'picture' :
27338                 this.beforeSelectFile(e);
27339                 break;
27340             case 'trash' :
27341                 this.trash(e);
27342                 break;
27343             case 'crop' :
27344                 this.crop(e);
27345                 break;
27346             case 'download' :
27347                 this.download(e);
27348                 break;
27349             default :
27350                 break;
27351         }
27352         
27353         this.fireEvent('footerbuttonclick', this, type);
27354     },
27355     
27356     beforeSelectFile : function(e)
27357     {
27358         e.preventDefault();
27359         
27360         if(this.fireEvent('beforeselectfile', this) != false){
27361             this.selectorEl.dom.click();
27362         }
27363     },
27364     
27365     onFileSelected : function(e)
27366     {
27367         e.preventDefault();
27368         
27369         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27370             return;
27371         }
27372         
27373         var file = this.selectorEl.dom.files[0];
27374         
27375         if(this.fireEvent('inspect', this, file) != false){
27376             this.prepare(file);
27377         }
27378         
27379     },
27380     
27381     trash : function(e)
27382     {
27383         this.fireEvent('trash', this);
27384     },
27385     
27386     download : function(e)
27387     {
27388         this.fireEvent('download', this);
27389     },
27390     
27391     loadCanvas : function(src)
27392     {   
27393         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27394             
27395             this.reset();
27396             
27397             this.imageEl = document.createElement('img');
27398             
27399             var _this = this;
27400             
27401             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27402             
27403             this.imageEl.src = src;
27404         }
27405     },
27406     
27407     onLoadCanvas : function()
27408     {   
27409         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27410         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27411         
27412         this.bodyEl.un('click', this.beforeSelectFile, this);
27413         
27414         this.notifyEl.hide();
27415         this.thumbEl.show();
27416         this.footerEl.show();
27417         
27418         this.baseRotateLevel();
27419         
27420         if(this.isDocument){
27421             this.setThumbBoxSize();
27422         }
27423         
27424         this.setThumbBoxPosition();
27425         
27426         this.baseScaleLevel();
27427         
27428         this.draw();
27429         
27430         this.resize();
27431         
27432         this.canvasLoaded = true;
27433         
27434         if(this.loadMask){
27435             this.maskEl.unmask();
27436         }
27437         
27438     },
27439     
27440     setCanvasPosition : function()
27441     {   
27442         if(!this.canvasEl){
27443             return;
27444         }
27445         
27446         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27447         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27448         
27449         this.previewEl.setLeft(pw);
27450         this.previewEl.setTop(ph);
27451         
27452     },
27453     
27454     onMouseDown : function(e)
27455     {   
27456         e.stopEvent();
27457         
27458         this.dragable = true;
27459         this.pinching = false;
27460         
27461         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27462             this.dragable = false;
27463             return;
27464         }
27465         
27466         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27467         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27468         
27469     },
27470     
27471     onMouseMove : function(e)
27472     {   
27473         e.stopEvent();
27474         
27475         if(!this.canvasLoaded){
27476             return;
27477         }
27478         
27479         if (!this.dragable){
27480             return;
27481         }
27482         
27483         var minX = Math.ceil(this.thumbEl.getLeft(true));
27484         var minY = Math.ceil(this.thumbEl.getTop(true));
27485         
27486         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27487         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27488         
27489         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27490         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27491         
27492         x = x - this.mouseX;
27493         y = y - this.mouseY;
27494         
27495         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27496         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27497         
27498         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27499         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27500         
27501         this.previewEl.setLeft(bgX);
27502         this.previewEl.setTop(bgY);
27503         
27504         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27505         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27506     },
27507     
27508     onMouseUp : function(e)
27509     {   
27510         e.stopEvent();
27511         
27512         this.dragable = false;
27513     },
27514     
27515     onMouseWheel : function(e)
27516     {   
27517         e.stopEvent();
27518         
27519         this.startScale = this.scale;
27520         
27521         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27522         
27523         if(!this.zoomable()){
27524             this.scale = this.startScale;
27525             return;
27526         }
27527         
27528         this.draw();
27529         
27530         return;
27531     },
27532     
27533     zoomable : function()
27534     {
27535         var minScale = this.thumbEl.getWidth() / this.minWidth;
27536         
27537         if(this.minWidth < this.minHeight){
27538             minScale = this.thumbEl.getHeight() / this.minHeight;
27539         }
27540         
27541         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27542         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27543         
27544         if(
27545                 this.isDocument &&
27546                 (this.rotate == 0 || this.rotate == 180) && 
27547                 (
27548                     width > this.imageEl.OriginWidth || 
27549                     height > this.imageEl.OriginHeight ||
27550                     (width < this.minWidth && height < this.minHeight)
27551                 )
27552         ){
27553             return false;
27554         }
27555         
27556         if(
27557                 this.isDocument &&
27558                 (this.rotate == 90 || this.rotate == 270) && 
27559                 (
27560                     width > this.imageEl.OriginWidth || 
27561                     height > this.imageEl.OriginHeight ||
27562                     (width < this.minHeight && height < this.minWidth)
27563                 )
27564         ){
27565             return false;
27566         }
27567         
27568         if(
27569                 !this.isDocument &&
27570                 (this.rotate == 0 || this.rotate == 180) && 
27571                 (
27572                     width < this.minWidth || 
27573                     width > this.imageEl.OriginWidth || 
27574                     height < this.minHeight || 
27575                     height > this.imageEl.OriginHeight
27576                 )
27577         ){
27578             return false;
27579         }
27580         
27581         if(
27582                 !this.isDocument &&
27583                 (this.rotate == 90 || this.rotate == 270) && 
27584                 (
27585                     width < this.minHeight || 
27586                     width > this.imageEl.OriginWidth || 
27587                     height < this.minWidth || 
27588                     height > this.imageEl.OriginHeight
27589                 )
27590         ){
27591             return false;
27592         }
27593         
27594         return true;
27595         
27596     },
27597     
27598     onRotateLeft : function(e)
27599     {   
27600         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27601             
27602             var minScale = this.thumbEl.getWidth() / this.minWidth;
27603             
27604             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27605             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27606             
27607             this.startScale = this.scale;
27608             
27609             while (this.getScaleLevel() < minScale){
27610             
27611                 this.scale = this.scale + 1;
27612                 
27613                 if(!this.zoomable()){
27614                     break;
27615                 }
27616                 
27617                 if(
27618                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27619                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27620                 ){
27621                     continue;
27622                 }
27623                 
27624                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27625
27626                 this.draw();
27627                 
27628                 return;
27629             }
27630             
27631             this.scale = this.startScale;
27632             
27633             this.onRotateFail();
27634             
27635             return false;
27636         }
27637         
27638         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27639
27640         if(this.isDocument){
27641             this.setThumbBoxSize();
27642             this.setThumbBoxPosition();
27643             this.setCanvasPosition();
27644         }
27645         
27646         this.draw();
27647         
27648         this.fireEvent('rotate', this, 'left');
27649         
27650     },
27651     
27652     onRotateRight : function(e)
27653     {
27654         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27655             
27656             var minScale = this.thumbEl.getWidth() / this.minWidth;
27657         
27658             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27659             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27660             
27661             this.startScale = this.scale;
27662             
27663             while (this.getScaleLevel() < minScale){
27664             
27665                 this.scale = this.scale + 1;
27666                 
27667                 if(!this.zoomable()){
27668                     break;
27669                 }
27670                 
27671                 if(
27672                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27673                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27674                 ){
27675                     continue;
27676                 }
27677                 
27678                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27679
27680                 this.draw();
27681                 
27682                 return;
27683             }
27684             
27685             this.scale = this.startScale;
27686             
27687             this.onRotateFail();
27688             
27689             return false;
27690         }
27691         
27692         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27693
27694         if(this.isDocument){
27695             this.setThumbBoxSize();
27696             this.setThumbBoxPosition();
27697             this.setCanvasPosition();
27698         }
27699         
27700         this.draw();
27701         
27702         this.fireEvent('rotate', this, 'right');
27703     },
27704     
27705     onRotateFail : function()
27706     {
27707         this.errorEl.show(true);
27708         
27709         var _this = this;
27710         
27711         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27712     },
27713     
27714     draw : function()
27715     {
27716         this.previewEl.dom.innerHTML = '';
27717         
27718         var canvasEl = document.createElement("canvas");
27719         
27720         var contextEl = canvasEl.getContext("2d");
27721         
27722         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27723         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27724         var center = this.imageEl.OriginWidth / 2;
27725         
27726         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27727             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27728             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27729             center = this.imageEl.OriginHeight / 2;
27730         }
27731         
27732         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27733         
27734         contextEl.translate(center, center);
27735         contextEl.rotate(this.rotate * Math.PI / 180);
27736
27737         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27738         
27739         this.canvasEl = document.createElement("canvas");
27740         
27741         this.contextEl = this.canvasEl.getContext("2d");
27742         
27743         switch (this.rotate) {
27744             case 0 :
27745                 
27746                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27747                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27748                 
27749                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27750                 
27751                 break;
27752             case 90 : 
27753                 
27754                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27755                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27756                 
27757                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27758                     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);
27759                     break;
27760                 }
27761                 
27762                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27763                 
27764                 break;
27765             case 180 :
27766                 
27767                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27768                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27769                 
27770                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27771                     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);
27772                     break;
27773                 }
27774                 
27775                 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);
27776                 
27777                 break;
27778             case 270 :
27779                 
27780                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27781                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27782         
27783                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27784                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27785                     break;
27786                 }
27787                 
27788                 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);
27789                 
27790                 break;
27791             default : 
27792                 break;
27793         }
27794         
27795         this.previewEl.appendChild(this.canvasEl);
27796         
27797         this.setCanvasPosition();
27798     },
27799     
27800     crop : function()
27801     {
27802         if(!this.canvasLoaded){
27803             return;
27804         }
27805         
27806         var imageCanvas = document.createElement("canvas");
27807         
27808         var imageContext = imageCanvas.getContext("2d");
27809         
27810         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27811         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27812         
27813         var center = imageCanvas.width / 2;
27814         
27815         imageContext.translate(center, center);
27816         
27817         imageContext.rotate(this.rotate * Math.PI / 180);
27818         
27819         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27820         
27821         var canvas = document.createElement("canvas");
27822         
27823         var context = canvas.getContext("2d");
27824                 
27825         canvas.width = this.minWidth;
27826         canvas.height = this.minHeight;
27827
27828         switch (this.rotate) {
27829             case 0 :
27830                 
27831                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27832                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27833                 
27834                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27835                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27836                 
27837                 var targetWidth = this.minWidth - 2 * x;
27838                 var targetHeight = this.minHeight - 2 * y;
27839                 
27840                 var scale = 1;
27841                 
27842                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27843                     scale = targetWidth / width;
27844                 }
27845                 
27846                 if(x > 0 && y == 0){
27847                     scale = targetHeight / height;
27848                 }
27849                 
27850                 if(x > 0 && y > 0){
27851                     scale = targetWidth / width;
27852                     
27853                     if(width < height){
27854                         scale = targetHeight / height;
27855                     }
27856                 }
27857                 
27858                 context.scale(scale, scale);
27859                 
27860                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27861                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27862
27863                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27864                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27865
27866                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27867                 
27868                 break;
27869             case 90 : 
27870                 
27871                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27872                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27873                 
27874                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27875                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27876                 
27877                 var targetWidth = this.minWidth - 2 * x;
27878                 var targetHeight = this.minHeight - 2 * y;
27879                 
27880                 var scale = 1;
27881                 
27882                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27883                     scale = targetWidth / width;
27884                 }
27885                 
27886                 if(x > 0 && y == 0){
27887                     scale = targetHeight / height;
27888                 }
27889                 
27890                 if(x > 0 && y > 0){
27891                     scale = targetWidth / width;
27892                     
27893                     if(width < height){
27894                         scale = targetHeight / height;
27895                     }
27896                 }
27897                 
27898                 context.scale(scale, scale);
27899                 
27900                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27901                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27902
27903                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27904                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27905                 
27906                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27907                 
27908                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27909                 
27910                 break;
27911             case 180 :
27912                 
27913                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27914                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27915                 
27916                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27917                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27918                 
27919                 var targetWidth = this.minWidth - 2 * x;
27920                 var targetHeight = this.minHeight - 2 * y;
27921                 
27922                 var scale = 1;
27923                 
27924                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27925                     scale = targetWidth / width;
27926                 }
27927                 
27928                 if(x > 0 && y == 0){
27929                     scale = targetHeight / height;
27930                 }
27931                 
27932                 if(x > 0 && y > 0){
27933                     scale = targetWidth / width;
27934                     
27935                     if(width < height){
27936                         scale = targetHeight / height;
27937                     }
27938                 }
27939                 
27940                 context.scale(scale, scale);
27941                 
27942                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27943                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27944
27945                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27946                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27947
27948                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27949                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27950                 
27951                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27952                 
27953                 break;
27954             case 270 :
27955                 
27956                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27957                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27958                 
27959                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27960                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27961                 
27962                 var targetWidth = this.minWidth - 2 * x;
27963                 var targetHeight = this.minHeight - 2 * y;
27964                 
27965                 var scale = 1;
27966                 
27967                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27968                     scale = targetWidth / width;
27969                 }
27970                 
27971                 if(x > 0 && y == 0){
27972                     scale = targetHeight / height;
27973                 }
27974                 
27975                 if(x > 0 && y > 0){
27976                     scale = targetWidth / width;
27977                     
27978                     if(width < height){
27979                         scale = targetHeight / height;
27980                     }
27981                 }
27982                 
27983                 context.scale(scale, scale);
27984                 
27985                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27986                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27987
27988                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27989                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27990                 
27991                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27992                 
27993                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27994                 
27995                 break;
27996             default : 
27997                 break;
27998         }
27999         
28000         this.cropData = canvas.toDataURL(this.cropType);
28001         
28002         if(this.fireEvent('crop', this, this.cropData) !== false){
28003             this.process(this.file, this.cropData);
28004         }
28005         
28006         return;
28007         
28008     },
28009     
28010     setThumbBoxSize : function()
28011     {
28012         var width, height;
28013         
28014         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28015             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28016             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28017             
28018             this.minWidth = width;
28019             this.minHeight = height;
28020             
28021             if(this.rotate == 90 || this.rotate == 270){
28022                 this.minWidth = height;
28023                 this.minHeight = width;
28024             }
28025         }
28026         
28027         height = 300;
28028         width = Math.ceil(this.minWidth * height / this.minHeight);
28029         
28030         if(this.minWidth > this.minHeight){
28031             width = 300;
28032             height = Math.ceil(this.minHeight * width / this.minWidth);
28033         }
28034         
28035         this.thumbEl.setStyle({
28036             width : width + 'px',
28037             height : height + 'px'
28038         });
28039
28040         return;
28041             
28042     },
28043     
28044     setThumbBoxPosition : function()
28045     {
28046         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28047         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28048         
28049         this.thumbEl.setLeft(x);
28050         this.thumbEl.setTop(y);
28051         
28052     },
28053     
28054     baseRotateLevel : function()
28055     {
28056         this.baseRotate = 1;
28057         
28058         if(
28059                 typeof(this.exif) != 'undefined' &&
28060                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28061                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28062         ){
28063             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28064         }
28065         
28066         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28067         
28068     },
28069     
28070     baseScaleLevel : function()
28071     {
28072         var width, height;
28073         
28074         if(this.isDocument){
28075             
28076             if(this.baseRotate == 6 || this.baseRotate == 8){
28077             
28078                 height = this.thumbEl.getHeight();
28079                 this.baseScale = height / this.imageEl.OriginWidth;
28080
28081                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28082                     width = this.thumbEl.getWidth();
28083                     this.baseScale = width / this.imageEl.OriginHeight;
28084                 }
28085
28086                 return;
28087             }
28088
28089             height = this.thumbEl.getHeight();
28090             this.baseScale = height / this.imageEl.OriginHeight;
28091
28092             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28093                 width = this.thumbEl.getWidth();
28094                 this.baseScale = width / this.imageEl.OriginWidth;
28095             }
28096
28097             return;
28098         }
28099         
28100         if(this.baseRotate == 6 || this.baseRotate == 8){
28101             
28102             width = this.thumbEl.getHeight();
28103             this.baseScale = width / this.imageEl.OriginHeight;
28104             
28105             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28106                 height = this.thumbEl.getWidth();
28107                 this.baseScale = height / this.imageEl.OriginHeight;
28108             }
28109             
28110             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28111                 height = this.thumbEl.getWidth();
28112                 this.baseScale = height / this.imageEl.OriginHeight;
28113                 
28114                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28115                     width = this.thumbEl.getHeight();
28116                     this.baseScale = width / this.imageEl.OriginWidth;
28117                 }
28118             }
28119             
28120             return;
28121         }
28122         
28123         width = this.thumbEl.getWidth();
28124         this.baseScale = width / this.imageEl.OriginWidth;
28125         
28126         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28127             height = this.thumbEl.getHeight();
28128             this.baseScale = height / this.imageEl.OriginHeight;
28129         }
28130         
28131         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28132             
28133             height = this.thumbEl.getHeight();
28134             this.baseScale = height / this.imageEl.OriginHeight;
28135             
28136             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28137                 width = this.thumbEl.getWidth();
28138                 this.baseScale = width / this.imageEl.OriginWidth;
28139             }
28140             
28141         }
28142         
28143         return;
28144     },
28145     
28146     getScaleLevel : function()
28147     {
28148         return this.baseScale * Math.pow(1.1, this.scale);
28149     },
28150     
28151     onTouchStart : function(e)
28152     {
28153         if(!this.canvasLoaded){
28154             this.beforeSelectFile(e);
28155             return;
28156         }
28157         
28158         var touches = e.browserEvent.touches;
28159         
28160         if(!touches){
28161             return;
28162         }
28163         
28164         if(touches.length == 1){
28165             this.onMouseDown(e);
28166             return;
28167         }
28168         
28169         if(touches.length != 2){
28170             return;
28171         }
28172         
28173         var coords = [];
28174         
28175         for(var i = 0, finger; finger = touches[i]; i++){
28176             coords.push(finger.pageX, finger.pageY);
28177         }
28178         
28179         var x = Math.pow(coords[0] - coords[2], 2);
28180         var y = Math.pow(coords[1] - coords[3], 2);
28181         
28182         this.startDistance = Math.sqrt(x + y);
28183         
28184         this.startScale = this.scale;
28185         
28186         this.pinching = true;
28187         this.dragable = false;
28188         
28189     },
28190     
28191     onTouchMove : function(e)
28192     {
28193         if(!this.pinching && !this.dragable){
28194             return;
28195         }
28196         
28197         var touches = e.browserEvent.touches;
28198         
28199         if(!touches){
28200             return;
28201         }
28202         
28203         if(this.dragable){
28204             this.onMouseMove(e);
28205             return;
28206         }
28207         
28208         var coords = [];
28209         
28210         for(var i = 0, finger; finger = touches[i]; i++){
28211             coords.push(finger.pageX, finger.pageY);
28212         }
28213         
28214         var x = Math.pow(coords[0] - coords[2], 2);
28215         var y = Math.pow(coords[1] - coords[3], 2);
28216         
28217         this.endDistance = Math.sqrt(x + y);
28218         
28219         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28220         
28221         if(!this.zoomable()){
28222             this.scale = this.startScale;
28223             return;
28224         }
28225         
28226         this.draw();
28227         
28228     },
28229     
28230     onTouchEnd : function(e)
28231     {
28232         this.pinching = false;
28233         this.dragable = false;
28234         
28235     },
28236     
28237     process : function(file, crop)
28238     {
28239         if(this.loadMask){
28240             this.maskEl.mask(this.loadingText);
28241         }
28242         
28243         this.xhr = new XMLHttpRequest();
28244         
28245         file.xhr = this.xhr;
28246
28247         this.xhr.open(this.method, this.url, true);
28248         
28249         var headers = {
28250             "Accept": "application/json",
28251             "Cache-Control": "no-cache",
28252             "X-Requested-With": "XMLHttpRequest"
28253         };
28254         
28255         for (var headerName in headers) {
28256             var headerValue = headers[headerName];
28257             if (headerValue) {
28258                 this.xhr.setRequestHeader(headerName, headerValue);
28259             }
28260         }
28261         
28262         var _this = this;
28263         
28264         this.xhr.onload = function()
28265         {
28266             _this.xhrOnLoad(_this.xhr);
28267         }
28268         
28269         this.xhr.onerror = function()
28270         {
28271             _this.xhrOnError(_this.xhr);
28272         }
28273         
28274         var formData = new FormData();
28275
28276         formData.append('returnHTML', 'NO');
28277         
28278         if(crop){
28279             formData.append('crop', crop);
28280         }
28281         
28282         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28283             formData.append(this.paramName, file, file.name);
28284         }
28285         
28286         if(typeof(file.filename) != 'undefined'){
28287             formData.append('filename', file.filename);
28288         }
28289         
28290         if(typeof(file.mimetype) != 'undefined'){
28291             formData.append('mimetype', file.mimetype);
28292         }
28293         
28294         if(this.fireEvent('arrange', this, formData) != false){
28295             this.xhr.send(formData);
28296         };
28297     },
28298     
28299     xhrOnLoad : function(xhr)
28300     {
28301         if(this.loadMask){
28302             this.maskEl.unmask();
28303         }
28304         
28305         if (xhr.readyState !== 4) {
28306             this.fireEvent('exception', this, xhr);
28307             return;
28308         }
28309
28310         var response = Roo.decode(xhr.responseText);
28311         
28312         if(!response.success){
28313             this.fireEvent('exception', this, xhr);
28314             return;
28315         }
28316         
28317         var response = Roo.decode(xhr.responseText);
28318         
28319         this.fireEvent('upload', this, response);
28320         
28321     },
28322     
28323     xhrOnError : function()
28324     {
28325         if(this.loadMask){
28326             this.maskEl.unmask();
28327         }
28328         
28329         Roo.log('xhr on error');
28330         
28331         var response = Roo.decode(xhr.responseText);
28332           
28333         Roo.log(response);
28334         
28335     },
28336     
28337     prepare : function(file)
28338     {   
28339         if(this.loadMask){
28340             this.maskEl.mask(this.loadingText);
28341         }
28342         
28343         this.file = false;
28344         this.exif = {};
28345         
28346         if(typeof(file) === 'string'){
28347             this.loadCanvas(file);
28348             return;
28349         }
28350         
28351         if(!file || !this.urlAPI){
28352             return;
28353         }
28354         
28355         this.file = file;
28356         this.cropType = file.type;
28357         
28358         var _this = this;
28359         
28360         if(this.fireEvent('prepare', this, this.file) != false){
28361             
28362             var reader = new FileReader();
28363             
28364             reader.onload = function (e) {
28365                 if (e.target.error) {
28366                     Roo.log(e.target.error);
28367                     return;
28368                 }
28369                 
28370                 var buffer = e.target.result,
28371                     dataView = new DataView(buffer),
28372                     offset = 2,
28373                     maxOffset = dataView.byteLength - 4,
28374                     markerBytes,
28375                     markerLength;
28376                 
28377                 if (dataView.getUint16(0) === 0xffd8) {
28378                     while (offset < maxOffset) {
28379                         markerBytes = dataView.getUint16(offset);
28380                         
28381                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28382                             markerLength = dataView.getUint16(offset + 2) + 2;
28383                             if (offset + markerLength > dataView.byteLength) {
28384                                 Roo.log('Invalid meta data: Invalid segment size.');
28385                                 break;
28386                             }
28387                             
28388                             if(markerBytes == 0xffe1){
28389                                 _this.parseExifData(
28390                                     dataView,
28391                                     offset,
28392                                     markerLength
28393                                 );
28394                             }
28395                             
28396                             offset += markerLength;
28397                             
28398                             continue;
28399                         }
28400                         
28401                         break;
28402                     }
28403                     
28404                 }
28405                 
28406                 var url = _this.urlAPI.createObjectURL(_this.file);
28407                 
28408                 _this.loadCanvas(url);
28409                 
28410                 return;
28411             }
28412             
28413             reader.readAsArrayBuffer(this.file);
28414             
28415         }
28416         
28417     },
28418     
28419     parseExifData : function(dataView, offset, length)
28420     {
28421         var tiffOffset = offset + 10,
28422             littleEndian,
28423             dirOffset;
28424     
28425         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28426             // No Exif data, might be XMP data instead
28427             return;
28428         }
28429         
28430         // Check for the ASCII code for "Exif" (0x45786966):
28431         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28432             // No Exif data, might be XMP data instead
28433             return;
28434         }
28435         if (tiffOffset + 8 > dataView.byteLength) {
28436             Roo.log('Invalid Exif data: Invalid segment size.');
28437             return;
28438         }
28439         // Check for the two null bytes:
28440         if (dataView.getUint16(offset + 8) !== 0x0000) {
28441             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28442             return;
28443         }
28444         // Check the byte alignment:
28445         switch (dataView.getUint16(tiffOffset)) {
28446         case 0x4949:
28447             littleEndian = true;
28448             break;
28449         case 0x4D4D:
28450             littleEndian = false;
28451             break;
28452         default:
28453             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28454             return;
28455         }
28456         // Check for the TIFF tag marker (0x002A):
28457         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28458             Roo.log('Invalid Exif data: Missing TIFF marker.');
28459             return;
28460         }
28461         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28462         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28463         
28464         this.parseExifTags(
28465             dataView,
28466             tiffOffset,
28467             tiffOffset + dirOffset,
28468             littleEndian
28469         );
28470     },
28471     
28472     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28473     {
28474         var tagsNumber,
28475             dirEndOffset,
28476             i;
28477         if (dirOffset + 6 > dataView.byteLength) {
28478             Roo.log('Invalid Exif data: Invalid directory offset.');
28479             return;
28480         }
28481         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28482         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28483         if (dirEndOffset + 4 > dataView.byteLength) {
28484             Roo.log('Invalid Exif data: Invalid directory size.');
28485             return;
28486         }
28487         for (i = 0; i < tagsNumber; i += 1) {
28488             this.parseExifTag(
28489                 dataView,
28490                 tiffOffset,
28491                 dirOffset + 2 + 12 * i, // tag offset
28492                 littleEndian
28493             );
28494         }
28495         // Return the offset to the next directory:
28496         return dataView.getUint32(dirEndOffset, littleEndian);
28497     },
28498     
28499     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28500     {
28501         var tag = dataView.getUint16(offset, littleEndian);
28502         
28503         this.exif[tag] = this.getExifValue(
28504             dataView,
28505             tiffOffset,
28506             offset,
28507             dataView.getUint16(offset + 2, littleEndian), // tag type
28508             dataView.getUint32(offset + 4, littleEndian), // tag length
28509             littleEndian
28510         );
28511     },
28512     
28513     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28514     {
28515         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28516             tagSize,
28517             dataOffset,
28518             values,
28519             i,
28520             str,
28521             c;
28522     
28523         if (!tagType) {
28524             Roo.log('Invalid Exif data: Invalid tag type.');
28525             return;
28526         }
28527         
28528         tagSize = tagType.size * length;
28529         // Determine if the value is contained in the dataOffset bytes,
28530         // or if the value at the dataOffset is a pointer to the actual data:
28531         dataOffset = tagSize > 4 ?
28532                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28533         if (dataOffset + tagSize > dataView.byteLength) {
28534             Roo.log('Invalid Exif data: Invalid data offset.');
28535             return;
28536         }
28537         if (length === 1) {
28538             return tagType.getValue(dataView, dataOffset, littleEndian);
28539         }
28540         values = [];
28541         for (i = 0; i < length; i += 1) {
28542             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28543         }
28544         
28545         if (tagType.ascii) {
28546             str = '';
28547             // Concatenate the chars:
28548             for (i = 0; i < values.length; i += 1) {
28549                 c = values[i];
28550                 // Ignore the terminating NULL byte(s):
28551                 if (c === '\u0000') {
28552                     break;
28553                 }
28554                 str += c;
28555             }
28556             return str;
28557         }
28558         return values;
28559     }
28560     
28561 });
28562
28563 Roo.apply(Roo.bootstrap.UploadCropbox, {
28564     tags : {
28565         'Orientation': 0x0112
28566     },
28567     
28568     Orientation: {
28569             1: 0, //'top-left',
28570 //            2: 'top-right',
28571             3: 180, //'bottom-right',
28572 //            4: 'bottom-left',
28573 //            5: 'left-top',
28574             6: 90, //'right-top',
28575 //            7: 'right-bottom',
28576             8: 270 //'left-bottom'
28577     },
28578     
28579     exifTagTypes : {
28580         // byte, 8-bit unsigned int:
28581         1: {
28582             getValue: function (dataView, dataOffset) {
28583                 return dataView.getUint8(dataOffset);
28584             },
28585             size: 1
28586         },
28587         // ascii, 8-bit byte:
28588         2: {
28589             getValue: function (dataView, dataOffset) {
28590                 return String.fromCharCode(dataView.getUint8(dataOffset));
28591             },
28592             size: 1,
28593             ascii: true
28594         },
28595         // short, 16 bit int:
28596         3: {
28597             getValue: function (dataView, dataOffset, littleEndian) {
28598                 return dataView.getUint16(dataOffset, littleEndian);
28599             },
28600             size: 2
28601         },
28602         // long, 32 bit int:
28603         4: {
28604             getValue: function (dataView, dataOffset, littleEndian) {
28605                 return dataView.getUint32(dataOffset, littleEndian);
28606             },
28607             size: 4
28608         },
28609         // rational = two long values, first is numerator, second is denominator:
28610         5: {
28611             getValue: function (dataView, dataOffset, littleEndian) {
28612                 return dataView.getUint32(dataOffset, littleEndian) /
28613                     dataView.getUint32(dataOffset + 4, littleEndian);
28614             },
28615             size: 8
28616         },
28617         // slong, 32 bit signed int:
28618         9: {
28619             getValue: function (dataView, dataOffset, littleEndian) {
28620                 return dataView.getInt32(dataOffset, littleEndian);
28621             },
28622             size: 4
28623         },
28624         // srational, two slongs, first is numerator, second is denominator:
28625         10: {
28626             getValue: function (dataView, dataOffset, littleEndian) {
28627                 return dataView.getInt32(dataOffset, littleEndian) /
28628                     dataView.getInt32(dataOffset + 4, littleEndian);
28629             },
28630             size: 8
28631         }
28632     },
28633     
28634     footer : {
28635         STANDARD : [
28636             {
28637                 tag : 'div',
28638                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28639                 action : 'rotate-left',
28640                 cn : [
28641                     {
28642                         tag : 'button',
28643                         cls : 'btn btn-default',
28644                         html : '<i class="fa fa-undo"></i>'
28645                     }
28646                 ]
28647             },
28648             {
28649                 tag : 'div',
28650                 cls : 'btn-group roo-upload-cropbox-picture',
28651                 action : 'picture',
28652                 cn : [
28653                     {
28654                         tag : 'button',
28655                         cls : 'btn btn-default',
28656                         html : '<i class="fa fa-picture-o"></i>'
28657                     }
28658                 ]
28659             },
28660             {
28661                 tag : 'div',
28662                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28663                 action : 'rotate-right',
28664                 cn : [
28665                     {
28666                         tag : 'button',
28667                         cls : 'btn btn-default',
28668                         html : '<i class="fa fa-repeat"></i>'
28669                     }
28670                 ]
28671             }
28672         ],
28673         DOCUMENT : [
28674             {
28675                 tag : 'div',
28676                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28677                 action : 'rotate-left',
28678                 cn : [
28679                     {
28680                         tag : 'button',
28681                         cls : 'btn btn-default',
28682                         html : '<i class="fa fa-undo"></i>'
28683                     }
28684                 ]
28685             },
28686             {
28687                 tag : 'div',
28688                 cls : 'btn-group roo-upload-cropbox-download',
28689                 action : 'download',
28690                 cn : [
28691                     {
28692                         tag : 'button',
28693                         cls : 'btn btn-default',
28694                         html : '<i class="fa fa-download"></i>'
28695                     }
28696                 ]
28697             },
28698             {
28699                 tag : 'div',
28700                 cls : 'btn-group roo-upload-cropbox-crop',
28701                 action : 'crop',
28702                 cn : [
28703                     {
28704                         tag : 'button',
28705                         cls : 'btn btn-default',
28706                         html : '<i class="fa fa-crop"></i>'
28707                     }
28708                 ]
28709             },
28710             {
28711                 tag : 'div',
28712                 cls : 'btn-group roo-upload-cropbox-trash',
28713                 action : 'trash',
28714                 cn : [
28715                     {
28716                         tag : 'button',
28717                         cls : 'btn btn-default',
28718                         html : '<i class="fa fa-trash"></i>'
28719                     }
28720                 ]
28721             },
28722             {
28723                 tag : 'div',
28724                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28725                 action : 'rotate-right',
28726                 cn : [
28727                     {
28728                         tag : 'button',
28729                         cls : 'btn btn-default',
28730                         html : '<i class="fa fa-repeat"></i>'
28731                     }
28732                 ]
28733             }
28734         ],
28735         ROTATOR : [
28736             {
28737                 tag : 'div',
28738                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28739                 action : 'rotate-left',
28740                 cn : [
28741                     {
28742                         tag : 'button',
28743                         cls : 'btn btn-default',
28744                         html : '<i class="fa fa-undo"></i>'
28745                     }
28746                 ]
28747             },
28748             {
28749                 tag : 'div',
28750                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28751                 action : 'rotate-right',
28752                 cn : [
28753                     {
28754                         tag : 'button',
28755                         cls : 'btn btn-default',
28756                         html : '<i class="fa fa-repeat"></i>'
28757                     }
28758                 ]
28759             }
28760         ]
28761     }
28762 });
28763
28764 /*
28765 * Licence: LGPL
28766 */
28767
28768 /**
28769  * @class Roo.bootstrap.DocumentManager
28770  * @extends Roo.bootstrap.Component
28771  * Bootstrap DocumentManager class
28772  * @cfg {String} paramName default 'imageUpload'
28773  * @cfg {String} toolTipName default 'filename'
28774  * @cfg {String} method default POST
28775  * @cfg {String} url action url
28776  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28777  * @cfg {Boolean} multiple multiple upload default true
28778  * @cfg {Number} thumbSize default 300
28779  * @cfg {String} fieldLabel
28780  * @cfg {Number} labelWidth default 4
28781  * @cfg {String} labelAlign (left|top) default left
28782  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28783 * @cfg {Number} labellg set the width of label (1-12)
28784  * @cfg {Number} labelmd set the width of label (1-12)
28785  * @cfg {Number} labelsm set the width of label (1-12)
28786  * @cfg {Number} labelxs set the width of label (1-12)
28787  * 
28788  * @constructor
28789  * Create a new DocumentManager
28790  * @param {Object} config The config object
28791  */
28792
28793 Roo.bootstrap.DocumentManager = function(config){
28794     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28795     
28796     this.files = [];
28797     this.delegates = [];
28798     
28799     this.addEvents({
28800         /**
28801          * @event initial
28802          * Fire when initial the DocumentManager
28803          * @param {Roo.bootstrap.DocumentManager} this
28804          */
28805         "initial" : true,
28806         /**
28807          * @event inspect
28808          * inspect selected file
28809          * @param {Roo.bootstrap.DocumentManager} this
28810          * @param {File} file
28811          */
28812         "inspect" : true,
28813         /**
28814          * @event exception
28815          * Fire when xhr load exception
28816          * @param {Roo.bootstrap.DocumentManager} this
28817          * @param {XMLHttpRequest} xhr
28818          */
28819         "exception" : true,
28820         /**
28821          * @event afterupload
28822          * Fire when xhr load exception
28823          * @param {Roo.bootstrap.DocumentManager} this
28824          * @param {XMLHttpRequest} xhr
28825          */
28826         "afterupload" : true,
28827         /**
28828          * @event prepare
28829          * prepare the form data
28830          * @param {Roo.bootstrap.DocumentManager} this
28831          * @param {Object} formData
28832          */
28833         "prepare" : true,
28834         /**
28835          * @event remove
28836          * Fire when remove the file
28837          * @param {Roo.bootstrap.DocumentManager} this
28838          * @param {Object} file
28839          */
28840         "remove" : true,
28841         /**
28842          * @event refresh
28843          * Fire after refresh the file
28844          * @param {Roo.bootstrap.DocumentManager} this
28845          */
28846         "refresh" : true,
28847         /**
28848          * @event click
28849          * Fire after click the image
28850          * @param {Roo.bootstrap.DocumentManager} this
28851          * @param {Object} file
28852          */
28853         "click" : true,
28854         /**
28855          * @event edit
28856          * Fire when upload a image and editable set to true
28857          * @param {Roo.bootstrap.DocumentManager} this
28858          * @param {Object} file
28859          */
28860         "edit" : true,
28861         /**
28862          * @event beforeselectfile
28863          * Fire before select file
28864          * @param {Roo.bootstrap.DocumentManager} this
28865          */
28866         "beforeselectfile" : true,
28867         /**
28868          * @event process
28869          * Fire before process file
28870          * @param {Roo.bootstrap.DocumentManager} this
28871          * @param {Object} file
28872          */
28873         "process" : true,
28874         /**
28875          * @event previewrendered
28876          * Fire when preview rendered
28877          * @param {Roo.bootstrap.DocumentManager} this
28878          * @param {Object} file
28879          */
28880         "previewrendered" : true,
28881         /**
28882          */
28883         "previewResize" : true
28884         
28885     });
28886 };
28887
28888 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28889     
28890     boxes : 0,
28891     inputName : '',
28892     thumbSize : 300,
28893     multiple : true,
28894     files : false,
28895     method : 'POST',
28896     url : '',
28897     paramName : 'imageUpload',
28898     toolTipName : 'filename',
28899     fieldLabel : '',
28900     labelWidth : 4,
28901     labelAlign : 'left',
28902     editable : true,
28903     delegates : false,
28904     xhr : false, 
28905     
28906     labellg : 0,
28907     labelmd : 0,
28908     labelsm : 0,
28909     labelxs : 0,
28910     
28911     getAutoCreate : function()
28912     {   
28913         var managerWidget = {
28914             tag : 'div',
28915             cls : 'roo-document-manager',
28916             cn : [
28917                 {
28918                     tag : 'input',
28919                     cls : 'roo-document-manager-selector',
28920                     type : 'file'
28921                 },
28922                 {
28923                     tag : 'div',
28924                     cls : 'roo-document-manager-uploader',
28925                     cn : [
28926                         {
28927                             tag : 'div',
28928                             cls : 'roo-document-manager-upload-btn',
28929                             html : '<i class="fa fa-plus"></i>'
28930                         }
28931                     ]
28932                     
28933                 }
28934             ]
28935         };
28936         
28937         var content = [
28938             {
28939                 tag : 'div',
28940                 cls : 'column col-md-12',
28941                 cn : managerWidget
28942             }
28943         ];
28944         
28945         if(this.fieldLabel.length){
28946             
28947             content = [
28948                 {
28949                     tag : 'div',
28950                     cls : 'column col-md-12',
28951                     html : this.fieldLabel
28952                 },
28953                 {
28954                     tag : 'div',
28955                     cls : 'column col-md-12',
28956                     cn : managerWidget
28957                 }
28958             ];
28959
28960             if(this.labelAlign == 'left'){
28961                 content = [
28962                     {
28963                         tag : 'div',
28964                         cls : 'column',
28965                         html : this.fieldLabel
28966                     },
28967                     {
28968                         tag : 'div',
28969                         cls : 'column',
28970                         cn : managerWidget
28971                     }
28972                 ];
28973                 
28974                 if(this.labelWidth > 12){
28975                     content[0].style = "width: " + this.labelWidth + 'px';
28976                 }
28977
28978                 if(this.labelWidth < 13 && this.labelmd == 0){
28979                     this.labelmd = this.labelWidth;
28980                 }
28981
28982                 if(this.labellg > 0){
28983                     content[0].cls += ' col-lg-' + this.labellg;
28984                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28985                 }
28986
28987                 if(this.labelmd > 0){
28988                     content[0].cls += ' col-md-' + this.labelmd;
28989                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28990                 }
28991
28992                 if(this.labelsm > 0){
28993                     content[0].cls += ' col-sm-' + this.labelsm;
28994                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28995                 }
28996
28997                 if(this.labelxs > 0){
28998                     content[0].cls += ' col-xs-' + this.labelxs;
28999                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29000                 }
29001                 
29002             }
29003         }
29004         
29005         var cfg = {
29006             tag : 'div',
29007             cls : 'row clearfix',
29008             cn : content
29009         };
29010         
29011         return cfg;
29012         
29013     },
29014     
29015     initEvents : function()
29016     {
29017         this.managerEl = this.el.select('.roo-document-manager', true).first();
29018         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29019         
29020         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29021         this.selectorEl.hide();
29022         
29023         if(this.multiple){
29024             this.selectorEl.attr('multiple', 'multiple');
29025         }
29026         
29027         this.selectorEl.on('change', this.onFileSelected, this);
29028         
29029         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29030         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29031         
29032         this.uploader.on('click', this.onUploaderClick, this);
29033         
29034         this.renderProgressDialog();
29035         
29036         var _this = this;
29037         
29038         window.addEventListener("resize", function() { _this.refresh(); } );
29039         
29040         this.fireEvent('initial', this);
29041     },
29042     
29043     renderProgressDialog : function()
29044     {
29045         var _this = this;
29046         
29047         this.progressDialog = new Roo.bootstrap.Modal({
29048             cls : 'roo-document-manager-progress-dialog',
29049             allow_close : false,
29050             title : '',
29051             buttons : [
29052                 {
29053                     name  :'cancel',
29054                     weight : 'danger',
29055                     html : 'Cancel'
29056                 }
29057             ], 
29058             listeners : { 
29059                 btnclick : function() {
29060                     _this.uploadCancel();
29061                     this.hide();
29062                 }
29063             }
29064         });
29065          
29066         this.progressDialog.render(Roo.get(document.body));
29067          
29068         this.progress = new Roo.bootstrap.Progress({
29069             cls : 'roo-document-manager-progress',
29070             active : true,
29071             striped : true
29072         });
29073         
29074         this.progress.render(this.progressDialog.getChildContainer());
29075         
29076         this.progressBar = new Roo.bootstrap.ProgressBar({
29077             cls : 'roo-document-manager-progress-bar',
29078             aria_valuenow : 0,
29079             aria_valuemin : 0,
29080             aria_valuemax : 12,
29081             panel : 'success'
29082         });
29083         
29084         this.progressBar.render(this.progress.getChildContainer());
29085     },
29086     
29087     onUploaderClick : function(e)
29088     {
29089         e.preventDefault();
29090      
29091         if(this.fireEvent('beforeselectfile', this) != false){
29092             this.selectorEl.dom.click();
29093         }
29094         
29095     },
29096     
29097     onFileSelected : function(e)
29098     {
29099         e.preventDefault();
29100         
29101         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29102             return;
29103         }
29104         
29105         Roo.each(this.selectorEl.dom.files, function(file){
29106             if(this.fireEvent('inspect', this, file) != false){
29107                 this.files.push(file);
29108             }
29109         }, this);
29110         
29111         this.queue();
29112         
29113     },
29114     
29115     queue : function()
29116     {
29117         this.selectorEl.dom.value = '';
29118         
29119         if(!this.files || !this.files.length){
29120             return;
29121         }
29122         
29123         if(this.boxes > 0 && this.files.length > this.boxes){
29124             this.files = this.files.slice(0, this.boxes);
29125         }
29126         
29127         this.uploader.show();
29128         
29129         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29130             this.uploader.hide();
29131         }
29132         
29133         var _this = this;
29134         
29135         var files = [];
29136         
29137         var docs = [];
29138         
29139         Roo.each(this.files, function(file){
29140             
29141             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29142                 var f = this.renderPreview(file);
29143                 files.push(f);
29144                 return;
29145             }
29146             
29147             if(file.type.indexOf('image') != -1){
29148                 this.delegates.push(
29149                     (function(){
29150                         _this.process(file);
29151                     }).createDelegate(this)
29152                 );
29153         
29154                 return;
29155             }
29156             
29157             docs.push(
29158                 (function(){
29159                     _this.process(file);
29160                 }).createDelegate(this)
29161             );
29162             
29163         }, this);
29164         
29165         this.files = files;
29166         
29167         this.delegates = this.delegates.concat(docs);
29168         
29169         if(!this.delegates.length){
29170             this.refresh();
29171             return;
29172         }
29173         
29174         this.progressBar.aria_valuemax = this.delegates.length;
29175         
29176         this.arrange();
29177         
29178         return;
29179     },
29180     
29181     arrange : function()
29182     {
29183         if(!this.delegates.length){
29184             this.progressDialog.hide();
29185             this.refresh();
29186             return;
29187         }
29188         
29189         var delegate = this.delegates.shift();
29190         
29191         this.progressDialog.show();
29192         
29193         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29194         
29195         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29196         
29197         delegate();
29198     },
29199     
29200     refresh : function()
29201     {
29202         this.uploader.show();
29203         
29204         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29205             this.uploader.hide();
29206         }
29207         
29208         Roo.isTouch ? this.closable(false) : this.closable(true);
29209         
29210         this.fireEvent('refresh', this);
29211     },
29212     
29213     onRemove : function(e, el, o)
29214     {
29215         e.preventDefault();
29216         
29217         this.fireEvent('remove', this, o);
29218         
29219     },
29220     
29221     remove : function(o)
29222     {
29223         var files = [];
29224         
29225         Roo.each(this.files, function(file){
29226             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29227                 files.push(file);
29228                 return;
29229             }
29230
29231             o.target.remove();
29232
29233         }, this);
29234         
29235         this.files = files;
29236         
29237         this.refresh();
29238     },
29239     
29240     clear : function()
29241     {
29242         Roo.each(this.files, function(file){
29243             if(!file.target){
29244                 return;
29245             }
29246             
29247             file.target.remove();
29248
29249         }, this);
29250         
29251         this.files = [];
29252         
29253         this.refresh();
29254     },
29255     
29256     onClick : function(e, el, o)
29257     {
29258         e.preventDefault();
29259         
29260         this.fireEvent('click', this, o);
29261         
29262     },
29263     
29264     closable : function(closable)
29265     {
29266         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29267             
29268             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29269             
29270             if(closable){
29271                 el.show();
29272                 return;
29273             }
29274             
29275             el.hide();
29276             
29277         }, this);
29278     },
29279     
29280     xhrOnLoad : function(xhr)
29281     {
29282         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29283             el.remove();
29284         }, this);
29285         
29286         if (xhr.readyState !== 4) {
29287             this.arrange();
29288             this.fireEvent('exception', this, xhr);
29289             return;
29290         }
29291
29292         var response = Roo.decode(xhr.responseText);
29293         
29294         if(!response.success){
29295             this.arrange();
29296             this.fireEvent('exception', this, xhr);
29297             return;
29298         }
29299         
29300         var file = this.renderPreview(response.data);
29301         
29302         this.files.push(file);
29303         
29304         this.arrange();
29305         
29306         this.fireEvent('afterupload', this, xhr);
29307         
29308     },
29309     
29310     xhrOnError : function(xhr)
29311     {
29312         Roo.log('xhr on error');
29313         
29314         var response = Roo.decode(xhr.responseText);
29315           
29316         Roo.log(response);
29317         
29318         this.arrange();
29319     },
29320     
29321     process : function(file)
29322     {
29323         if(this.fireEvent('process', this, file) !== false){
29324             if(this.editable && file.type.indexOf('image') != -1){
29325                 this.fireEvent('edit', this, file);
29326                 return;
29327             }
29328
29329             this.uploadStart(file, false);
29330
29331             return;
29332         }
29333         
29334     },
29335     
29336     uploadStart : function(file, crop)
29337     {
29338         this.xhr = new XMLHttpRequest();
29339         
29340         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29341             this.arrange();
29342             return;
29343         }
29344         
29345         file.xhr = this.xhr;
29346             
29347         this.managerEl.createChild({
29348             tag : 'div',
29349             cls : 'roo-document-manager-loading',
29350             cn : [
29351                 {
29352                     tag : 'div',
29353                     tooltip : file.name,
29354                     cls : 'roo-document-manager-thumb',
29355                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29356                 }
29357             ]
29358
29359         });
29360
29361         this.xhr.open(this.method, this.url, true);
29362         
29363         var headers = {
29364             "Accept": "application/json",
29365             "Cache-Control": "no-cache",
29366             "X-Requested-With": "XMLHttpRequest"
29367         };
29368         
29369         for (var headerName in headers) {
29370             var headerValue = headers[headerName];
29371             if (headerValue) {
29372                 this.xhr.setRequestHeader(headerName, headerValue);
29373             }
29374         }
29375         
29376         var _this = this;
29377         
29378         this.xhr.onload = function()
29379         {
29380             _this.xhrOnLoad(_this.xhr);
29381         }
29382         
29383         this.xhr.onerror = function()
29384         {
29385             _this.xhrOnError(_this.xhr);
29386         }
29387         
29388         var formData = new FormData();
29389
29390         formData.append('returnHTML', 'NO');
29391         
29392         if(crop){
29393             formData.append('crop', crop);
29394         }
29395         
29396         formData.append(this.paramName, file, file.name);
29397         
29398         var options = {
29399             file : file, 
29400             manually : false
29401         };
29402         
29403         if(this.fireEvent('prepare', this, formData, options) != false){
29404             
29405             if(options.manually){
29406                 return;
29407             }
29408             
29409             this.xhr.send(formData);
29410             return;
29411         };
29412         
29413         this.uploadCancel();
29414     },
29415     
29416     uploadCancel : function()
29417     {
29418         if (this.xhr) {
29419             this.xhr.abort();
29420         }
29421         
29422         this.delegates = [];
29423         
29424         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29425             el.remove();
29426         }, this);
29427         
29428         this.arrange();
29429     },
29430     
29431     renderPreview : function(file)
29432     {
29433         if(typeof(file.target) != 'undefined' && file.target){
29434             return file;
29435         }
29436         
29437         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29438         
29439         var previewEl = this.managerEl.createChild({
29440             tag : 'div',
29441             cls : 'roo-document-manager-preview',
29442             cn : [
29443                 {
29444                     tag : 'div',
29445                     tooltip : file[this.toolTipName],
29446                     cls : 'roo-document-manager-thumb',
29447                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29448                 },
29449                 {
29450                     tag : 'button',
29451                     cls : 'close',
29452                     html : '<i class="fa fa-times-circle"></i>'
29453                 }
29454             ]
29455         });
29456
29457         var close = previewEl.select('button.close', true).first();
29458
29459         close.on('click', this.onRemove, this, file);
29460
29461         file.target = previewEl;
29462
29463         var image = previewEl.select('img', true).first();
29464         
29465         var _this = this;
29466         
29467         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29468         
29469         image.on('click', this.onClick, this, file);
29470         
29471         this.fireEvent('previewrendered', this, file);
29472         
29473         return file;
29474         
29475     },
29476     
29477     onPreviewLoad : function(file, image)
29478     {
29479         if(typeof(file.target) == 'undefined' || !file.target){
29480             return;
29481         }
29482         
29483         var width = image.dom.naturalWidth || image.dom.width;
29484         var height = image.dom.naturalHeight || image.dom.height;
29485         
29486         if(!this.previewResize) {
29487             return;
29488         }
29489         
29490         if(width > height){
29491             file.target.addClass('wide');
29492             return;
29493         }
29494         
29495         file.target.addClass('tall');
29496         return;
29497         
29498     },
29499     
29500     uploadFromSource : function(file, crop)
29501     {
29502         this.xhr = new XMLHttpRequest();
29503         
29504         this.managerEl.createChild({
29505             tag : 'div',
29506             cls : 'roo-document-manager-loading',
29507             cn : [
29508                 {
29509                     tag : 'div',
29510                     tooltip : file.name,
29511                     cls : 'roo-document-manager-thumb',
29512                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29513                 }
29514             ]
29515
29516         });
29517
29518         this.xhr.open(this.method, this.url, true);
29519         
29520         var headers = {
29521             "Accept": "application/json",
29522             "Cache-Control": "no-cache",
29523             "X-Requested-With": "XMLHttpRequest"
29524         };
29525         
29526         for (var headerName in headers) {
29527             var headerValue = headers[headerName];
29528             if (headerValue) {
29529                 this.xhr.setRequestHeader(headerName, headerValue);
29530             }
29531         }
29532         
29533         var _this = this;
29534         
29535         this.xhr.onload = function()
29536         {
29537             _this.xhrOnLoad(_this.xhr);
29538         }
29539         
29540         this.xhr.onerror = function()
29541         {
29542             _this.xhrOnError(_this.xhr);
29543         }
29544         
29545         var formData = new FormData();
29546
29547         formData.append('returnHTML', 'NO');
29548         
29549         formData.append('crop', crop);
29550         
29551         if(typeof(file.filename) != 'undefined'){
29552             formData.append('filename', file.filename);
29553         }
29554         
29555         if(typeof(file.mimetype) != 'undefined'){
29556             formData.append('mimetype', file.mimetype);
29557         }
29558         
29559         Roo.log(formData);
29560         
29561         if(this.fireEvent('prepare', this, formData) != false){
29562             this.xhr.send(formData);
29563         };
29564     }
29565 });
29566
29567 /*
29568 * Licence: LGPL
29569 */
29570
29571 /**
29572  * @class Roo.bootstrap.DocumentViewer
29573  * @extends Roo.bootstrap.Component
29574  * Bootstrap DocumentViewer class
29575  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29576  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29577  * 
29578  * @constructor
29579  * Create a new DocumentViewer
29580  * @param {Object} config The config object
29581  */
29582
29583 Roo.bootstrap.DocumentViewer = function(config){
29584     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29585     
29586     this.addEvents({
29587         /**
29588          * @event initial
29589          * Fire after initEvent
29590          * @param {Roo.bootstrap.DocumentViewer} this
29591          */
29592         "initial" : true,
29593         /**
29594          * @event click
29595          * Fire after click
29596          * @param {Roo.bootstrap.DocumentViewer} this
29597          */
29598         "click" : true,
29599         /**
29600          * @event download
29601          * Fire after download button
29602          * @param {Roo.bootstrap.DocumentViewer} this
29603          */
29604         "download" : true,
29605         /**
29606          * @event trash
29607          * Fire after trash button
29608          * @param {Roo.bootstrap.DocumentViewer} this
29609          */
29610         "trash" : true
29611         
29612     });
29613 };
29614
29615 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29616     
29617     showDownload : true,
29618     
29619     showTrash : true,
29620     
29621     getAutoCreate : function()
29622     {
29623         var cfg = {
29624             tag : 'div',
29625             cls : 'roo-document-viewer',
29626             cn : [
29627                 {
29628                     tag : 'div',
29629                     cls : 'roo-document-viewer-body',
29630                     cn : [
29631                         {
29632                             tag : 'div',
29633                             cls : 'roo-document-viewer-thumb',
29634                             cn : [
29635                                 {
29636                                     tag : 'img',
29637                                     cls : 'roo-document-viewer-image'
29638                                 }
29639                             ]
29640                         }
29641                     ]
29642                 },
29643                 {
29644                     tag : 'div',
29645                     cls : 'roo-document-viewer-footer',
29646                     cn : {
29647                         tag : 'div',
29648                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29649                         cn : [
29650                             {
29651                                 tag : 'div',
29652                                 cls : 'btn-group roo-document-viewer-download',
29653                                 cn : [
29654                                     {
29655                                         tag : 'button',
29656                                         cls : 'btn btn-default',
29657                                         html : '<i class="fa fa-download"></i>'
29658                                     }
29659                                 ]
29660                             },
29661                             {
29662                                 tag : 'div',
29663                                 cls : 'btn-group roo-document-viewer-trash',
29664                                 cn : [
29665                                     {
29666                                         tag : 'button',
29667                                         cls : 'btn btn-default',
29668                                         html : '<i class="fa fa-trash"></i>'
29669                                     }
29670                                 ]
29671                             }
29672                         ]
29673                     }
29674                 }
29675             ]
29676         };
29677         
29678         return cfg;
29679     },
29680     
29681     initEvents : function()
29682     {
29683         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29684         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29685         
29686         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29687         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29688         
29689         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29690         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29691         
29692         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29693         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29694         
29695         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29696         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29697         
29698         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29699         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29700         
29701         this.bodyEl.on('click', this.onClick, this);
29702         this.downloadBtn.on('click', this.onDownload, this);
29703         this.trashBtn.on('click', this.onTrash, this);
29704         
29705         this.downloadBtn.hide();
29706         this.trashBtn.hide();
29707         
29708         if(this.showDownload){
29709             this.downloadBtn.show();
29710         }
29711         
29712         if(this.showTrash){
29713             this.trashBtn.show();
29714         }
29715         
29716         if(!this.showDownload && !this.showTrash) {
29717             this.footerEl.hide();
29718         }
29719         
29720     },
29721     
29722     initial : function()
29723     {
29724         this.fireEvent('initial', this);
29725         
29726     },
29727     
29728     onClick : function(e)
29729     {
29730         e.preventDefault();
29731         
29732         this.fireEvent('click', this);
29733     },
29734     
29735     onDownload : function(e)
29736     {
29737         e.preventDefault();
29738         
29739         this.fireEvent('download', this);
29740     },
29741     
29742     onTrash : function(e)
29743     {
29744         e.preventDefault();
29745         
29746         this.fireEvent('trash', this);
29747     }
29748     
29749 });
29750 /*
29751  * - LGPL
29752  *
29753  * nav progress bar
29754  * 
29755  */
29756
29757 /**
29758  * @class Roo.bootstrap.NavProgressBar
29759  * @extends Roo.bootstrap.Component
29760  * Bootstrap NavProgressBar class
29761  * 
29762  * @constructor
29763  * Create a new nav progress bar
29764  * @param {Object} config The config object
29765  */
29766
29767 Roo.bootstrap.NavProgressBar = function(config){
29768     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29769
29770     this.bullets = this.bullets || [];
29771    
29772 //    Roo.bootstrap.NavProgressBar.register(this);
29773      this.addEvents({
29774         /**
29775              * @event changed
29776              * Fires when the active item changes
29777              * @param {Roo.bootstrap.NavProgressBar} this
29778              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29779              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29780          */
29781         'changed': true
29782      });
29783     
29784 };
29785
29786 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29787     
29788     bullets : [],
29789     barItems : [],
29790     
29791     getAutoCreate : function()
29792     {
29793         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29794         
29795         cfg = {
29796             tag : 'div',
29797             cls : 'roo-navigation-bar-group',
29798             cn : [
29799                 {
29800                     tag : 'div',
29801                     cls : 'roo-navigation-top-bar'
29802                 },
29803                 {
29804                     tag : 'div',
29805                     cls : 'roo-navigation-bullets-bar',
29806                     cn : [
29807                         {
29808                             tag : 'ul',
29809                             cls : 'roo-navigation-bar'
29810                         }
29811                     ]
29812                 },
29813                 
29814                 {
29815                     tag : 'div',
29816                     cls : 'roo-navigation-bottom-bar'
29817                 }
29818             ]
29819             
29820         };
29821         
29822         return cfg;
29823         
29824     },
29825     
29826     initEvents: function() 
29827     {
29828         
29829     },
29830     
29831     onRender : function(ct, position) 
29832     {
29833         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29834         
29835         if(this.bullets.length){
29836             Roo.each(this.bullets, function(b){
29837                this.addItem(b);
29838             }, this);
29839         }
29840         
29841         this.format();
29842         
29843     },
29844     
29845     addItem : function(cfg)
29846     {
29847         var item = new Roo.bootstrap.NavProgressItem(cfg);
29848         
29849         item.parentId = this.id;
29850         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29851         
29852         if(cfg.html){
29853             var top = new Roo.bootstrap.Element({
29854                 tag : 'div',
29855                 cls : 'roo-navigation-bar-text'
29856             });
29857             
29858             var bottom = new Roo.bootstrap.Element({
29859                 tag : 'div',
29860                 cls : 'roo-navigation-bar-text'
29861             });
29862             
29863             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29864             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29865             
29866             var topText = new Roo.bootstrap.Element({
29867                 tag : 'span',
29868                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29869             });
29870             
29871             var bottomText = new Roo.bootstrap.Element({
29872                 tag : 'span',
29873                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29874             });
29875             
29876             topText.onRender(top.el, null);
29877             bottomText.onRender(bottom.el, null);
29878             
29879             item.topEl = top;
29880             item.bottomEl = bottom;
29881         }
29882         
29883         this.barItems.push(item);
29884         
29885         return item;
29886     },
29887     
29888     getActive : function()
29889     {
29890         var active = false;
29891         
29892         Roo.each(this.barItems, function(v){
29893             
29894             if (!v.isActive()) {
29895                 return;
29896             }
29897             
29898             active = v;
29899             return false;
29900             
29901         });
29902         
29903         return active;
29904     },
29905     
29906     setActiveItem : function(item)
29907     {
29908         var prev = false;
29909         
29910         Roo.each(this.barItems, function(v){
29911             if (v.rid == item.rid) {
29912                 return ;
29913             }
29914             
29915             if (v.isActive()) {
29916                 v.setActive(false);
29917                 prev = v;
29918             }
29919         });
29920
29921         item.setActive(true);
29922         
29923         this.fireEvent('changed', this, item, prev);
29924     },
29925     
29926     getBarItem: function(rid)
29927     {
29928         var ret = false;
29929         
29930         Roo.each(this.barItems, function(e) {
29931             if (e.rid != rid) {
29932                 return;
29933             }
29934             
29935             ret =  e;
29936             return false;
29937         });
29938         
29939         return ret;
29940     },
29941     
29942     indexOfItem : function(item)
29943     {
29944         var index = false;
29945         
29946         Roo.each(this.barItems, function(v, i){
29947             
29948             if (v.rid != item.rid) {
29949                 return;
29950             }
29951             
29952             index = i;
29953             return false
29954         });
29955         
29956         return index;
29957     },
29958     
29959     setActiveNext : function()
29960     {
29961         var i = this.indexOfItem(this.getActive());
29962         
29963         if (i > this.barItems.length) {
29964             return;
29965         }
29966         
29967         this.setActiveItem(this.barItems[i+1]);
29968     },
29969     
29970     setActivePrev : function()
29971     {
29972         var i = this.indexOfItem(this.getActive());
29973         
29974         if (i  < 1) {
29975             return;
29976         }
29977         
29978         this.setActiveItem(this.barItems[i-1]);
29979     },
29980     
29981     format : function()
29982     {
29983         if(!this.barItems.length){
29984             return;
29985         }
29986      
29987         var width = 100 / this.barItems.length;
29988         
29989         Roo.each(this.barItems, function(i){
29990             i.el.setStyle('width', width + '%');
29991             i.topEl.el.setStyle('width', width + '%');
29992             i.bottomEl.el.setStyle('width', width + '%');
29993         }, this);
29994         
29995     }
29996     
29997 });
29998 /*
29999  * - LGPL
30000  *
30001  * Nav Progress Item
30002  * 
30003  */
30004
30005 /**
30006  * @class Roo.bootstrap.NavProgressItem
30007  * @extends Roo.bootstrap.Component
30008  * Bootstrap NavProgressItem class
30009  * @cfg {String} rid the reference id
30010  * @cfg {Boolean} active (true|false) Is item active default false
30011  * @cfg {Boolean} disabled (true|false) Is item active default false
30012  * @cfg {String} html
30013  * @cfg {String} position (top|bottom) text position default bottom
30014  * @cfg {String} icon show icon instead of number
30015  * 
30016  * @constructor
30017  * Create a new NavProgressItem
30018  * @param {Object} config The config object
30019  */
30020 Roo.bootstrap.NavProgressItem = function(config){
30021     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30022     this.addEvents({
30023         // raw events
30024         /**
30025          * @event click
30026          * The raw click event for the entire grid.
30027          * @param {Roo.bootstrap.NavProgressItem} this
30028          * @param {Roo.EventObject} e
30029          */
30030         "click" : true
30031     });
30032    
30033 };
30034
30035 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30036     
30037     rid : '',
30038     active : false,
30039     disabled : false,
30040     html : '',
30041     position : 'bottom',
30042     icon : false,
30043     
30044     getAutoCreate : function()
30045     {
30046         var iconCls = 'roo-navigation-bar-item-icon';
30047         
30048         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30049         
30050         var cfg = {
30051             tag: 'li',
30052             cls: 'roo-navigation-bar-item',
30053             cn : [
30054                 {
30055                     tag : 'i',
30056                     cls : iconCls
30057                 }
30058             ]
30059         };
30060         
30061         if(this.active){
30062             cfg.cls += ' active';
30063         }
30064         if(this.disabled){
30065             cfg.cls += ' disabled';
30066         }
30067         
30068         return cfg;
30069     },
30070     
30071     disable : function()
30072     {
30073         this.setDisabled(true);
30074     },
30075     
30076     enable : function()
30077     {
30078         this.setDisabled(false);
30079     },
30080     
30081     initEvents: function() 
30082     {
30083         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30084         
30085         this.iconEl.on('click', this.onClick, this);
30086     },
30087     
30088     onClick : function(e)
30089     {
30090         e.preventDefault();
30091         
30092         if(this.disabled){
30093             return;
30094         }
30095         
30096         if(this.fireEvent('click', this, e) === false){
30097             return;
30098         };
30099         
30100         this.parent().setActiveItem(this);
30101     },
30102     
30103     isActive: function () 
30104     {
30105         return this.active;
30106     },
30107     
30108     setActive : function(state)
30109     {
30110         if(this.active == state){
30111             return;
30112         }
30113         
30114         this.active = state;
30115         
30116         if (state) {
30117             this.el.addClass('active');
30118             return;
30119         }
30120         
30121         this.el.removeClass('active');
30122         
30123         return;
30124     },
30125     
30126     setDisabled : function(state)
30127     {
30128         if(this.disabled == state){
30129             return;
30130         }
30131         
30132         this.disabled = state;
30133         
30134         if (state) {
30135             this.el.addClass('disabled');
30136             return;
30137         }
30138         
30139         this.el.removeClass('disabled');
30140     },
30141     
30142     tooltipEl : function()
30143     {
30144         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30145     }
30146 });
30147  
30148
30149  /*
30150  * - LGPL
30151  *
30152  * FieldLabel
30153  * 
30154  */
30155
30156 /**
30157  * @class Roo.bootstrap.FieldLabel
30158  * @extends Roo.bootstrap.Component
30159  * Bootstrap FieldLabel class
30160  * @cfg {String} html contents of the element
30161  * @cfg {String} tag tag of the element default label
30162  * @cfg {String} cls class of the element
30163  * @cfg {String} target label target 
30164  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30165  * @cfg {String} invalidClass default "text-warning"
30166  * @cfg {String} validClass default "text-success"
30167  * @cfg {String} iconTooltip default "This field is required"
30168  * @cfg {String} indicatorpos (left|right) default left
30169  * 
30170  * @constructor
30171  * Create a new FieldLabel
30172  * @param {Object} config The config object
30173  */
30174
30175 Roo.bootstrap.FieldLabel = function(config){
30176     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30177     
30178     this.addEvents({
30179             /**
30180              * @event invalid
30181              * Fires after the field has been marked as invalid.
30182              * @param {Roo.form.FieldLabel} this
30183              * @param {String} msg The validation message
30184              */
30185             invalid : true,
30186             /**
30187              * @event valid
30188              * Fires after the field has been validated with no errors.
30189              * @param {Roo.form.FieldLabel} this
30190              */
30191             valid : true
30192         });
30193 };
30194
30195 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30196     
30197     tag: 'label',
30198     cls: '',
30199     html: '',
30200     target: '',
30201     allowBlank : true,
30202     invalidClass : 'has-warning',
30203     validClass : 'has-success',
30204     iconTooltip : 'This field is required',
30205     indicatorpos : 'left',
30206     
30207     getAutoCreate : function(){
30208         
30209         var cls = "";
30210         if (!this.allowBlank) {
30211             cls  = "visible";
30212         }
30213         
30214         var cfg = {
30215             tag : this.tag,
30216             cls : 'roo-bootstrap-field-label ' + this.cls,
30217             for : this.target,
30218             cn : [
30219                 {
30220                     tag : 'i',
30221                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30222                     tooltip : this.iconTooltip
30223                 },
30224                 {
30225                     tag : 'span',
30226                     html : this.html
30227                 }
30228             ] 
30229         };
30230         
30231         if(this.indicatorpos == 'right'){
30232             var cfg = {
30233                 tag : this.tag,
30234                 cls : 'roo-bootstrap-field-label ' + this.cls,
30235                 for : this.target,
30236                 cn : [
30237                     {
30238                         tag : 'span',
30239                         html : this.html
30240                     },
30241                     {
30242                         tag : 'i',
30243                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30244                         tooltip : this.iconTooltip
30245                     }
30246                 ] 
30247             };
30248         }
30249         
30250         return cfg;
30251     },
30252     
30253     initEvents: function() 
30254     {
30255         Roo.bootstrap.Element.superclass.initEvents.call(this);
30256         
30257         this.indicator = this.indicatorEl();
30258         
30259         if(this.indicator){
30260             this.indicator.removeClass('visible');
30261             this.indicator.addClass('invisible');
30262         }
30263         
30264         Roo.bootstrap.FieldLabel.register(this);
30265     },
30266     
30267     indicatorEl : function()
30268     {
30269         var indicator = this.el.select('i.roo-required-indicator',true).first();
30270         
30271         if(!indicator){
30272             return false;
30273         }
30274         
30275         return indicator;
30276         
30277     },
30278     
30279     /**
30280      * Mark this field as valid
30281      */
30282     markValid : function()
30283     {
30284         if(this.indicator){
30285             this.indicator.removeClass('visible');
30286             this.indicator.addClass('invisible');
30287         }
30288         
30289         this.el.removeClass(this.invalidClass);
30290         
30291         this.el.addClass(this.validClass);
30292         
30293         this.fireEvent('valid', this);
30294     },
30295     
30296     /**
30297      * Mark this field as invalid
30298      * @param {String} msg The validation message
30299      */
30300     markInvalid : function(msg)
30301     {
30302         if(this.indicator){
30303             this.indicator.removeClass('invisible');
30304             this.indicator.addClass('visible');
30305         }
30306         
30307         this.el.removeClass(this.validClass);
30308         
30309         this.el.addClass(this.invalidClass);
30310         
30311         this.fireEvent('invalid', this, msg);
30312     }
30313     
30314    
30315 });
30316
30317 Roo.apply(Roo.bootstrap.FieldLabel, {
30318     
30319     groups: {},
30320     
30321      /**
30322     * register a FieldLabel Group
30323     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30324     */
30325     register : function(label)
30326     {
30327         if(this.groups.hasOwnProperty(label.target)){
30328             return;
30329         }
30330      
30331         this.groups[label.target] = label;
30332         
30333     },
30334     /**
30335     * fetch a FieldLabel Group based on the target
30336     * @param {string} target
30337     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30338     */
30339     get: function(target) {
30340         if (typeof(this.groups[target]) == 'undefined') {
30341             return false;
30342         }
30343         
30344         return this.groups[target] ;
30345     }
30346 });
30347
30348  
30349
30350  /*
30351  * - LGPL
30352  *
30353  * page DateSplitField.
30354  * 
30355  */
30356
30357
30358 /**
30359  * @class Roo.bootstrap.DateSplitField
30360  * @extends Roo.bootstrap.Component
30361  * Bootstrap DateSplitField class
30362  * @cfg {string} fieldLabel - the label associated
30363  * @cfg {Number} labelWidth set the width of label (0-12)
30364  * @cfg {String} labelAlign (top|left)
30365  * @cfg {Boolean} dayAllowBlank (true|false) default false
30366  * @cfg {Boolean} monthAllowBlank (true|false) default false
30367  * @cfg {Boolean} yearAllowBlank (true|false) default false
30368  * @cfg {string} dayPlaceholder 
30369  * @cfg {string} monthPlaceholder
30370  * @cfg {string} yearPlaceholder
30371  * @cfg {string} dayFormat default 'd'
30372  * @cfg {string} monthFormat default 'm'
30373  * @cfg {string} yearFormat default 'Y'
30374  * @cfg {Number} labellg set the width of label (1-12)
30375  * @cfg {Number} labelmd set the width of label (1-12)
30376  * @cfg {Number} labelsm set the width of label (1-12)
30377  * @cfg {Number} labelxs set the width of label (1-12)
30378
30379  *     
30380  * @constructor
30381  * Create a new DateSplitField
30382  * @param {Object} config The config object
30383  */
30384
30385 Roo.bootstrap.DateSplitField = function(config){
30386     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30387     
30388     this.addEvents({
30389         // raw events
30390          /**
30391          * @event years
30392          * getting the data of years
30393          * @param {Roo.bootstrap.DateSplitField} this
30394          * @param {Object} years
30395          */
30396         "years" : true,
30397         /**
30398          * @event days
30399          * getting the data of days
30400          * @param {Roo.bootstrap.DateSplitField} this
30401          * @param {Object} days
30402          */
30403         "days" : true,
30404         /**
30405          * @event invalid
30406          * Fires after the field has been marked as invalid.
30407          * @param {Roo.form.Field} this
30408          * @param {String} msg The validation message
30409          */
30410         invalid : true,
30411        /**
30412          * @event valid
30413          * Fires after the field has been validated with no errors.
30414          * @param {Roo.form.Field} this
30415          */
30416         valid : true
30417     });
30418 };
30419
30420 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30421     
30422     fieldLabel : '',
30423     labelAlign : 'top',
30424     labelWidth : 3,
30425     dayAllowBlank : false,
30426     monthAllowBlank : false,
30427     yearAllowBlank : false,
30428     dayPlaceholder : '',
30429     monthPlaceholder : '',
30430     yearPlaceholder : '',
30431     dayFormat : 'd',
30432     monthFormat : 'm',
30433     yearFormat : 'Y',
30434     isFormField : true,
30435     labellg : 0,
30436     labelmd : 0,
30437     labelsm : 0,
30438     labelxs : 0,
30439     
30440     getAutoCreate : function()
30441     {
30442         var cfg = {
30443             tag : 'div',
30444             cls : 'row roo-date-split-field-group',
30445             cn : [
30446                 {
30447                     tag : 'input',
30448                     type : 'hidden',
30449                     cls : 'form-hidden-field roo-date-split-field-group-value',
30450                     name : this.name
30451                 }
30452             ]
30453         };
30454         
30455         var labelCls = 'col-md-12';
30456         var contentCls = 'col-md-4';
30457         
30458         if(this.fieldLabel){
30459             
30460             var label = {
30461                 tag : 'div',
30462                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30463                 cn : [
30464                     {
30465                         tag : 'label',
30466                         html : this.fieldLabel
30467                     }
30468                 ]
30469             };
30470             
30471             if(this.labelAlign == 'left'){
30472             
30473                 if(this.labelWidth > 12){
30474                     label.style = "width: " + this.labelWidth + 'px';
30475                 }
30476
30477                 if(this.labelWidth < 13 && this.labelmd == 0){
30478                     this.labelmd = this.labelWidth;
30479                 }
30480
30481                 if(this.labellg > 0){
30482                     labelCls = ' col-lg-' + this.labellg;
30483                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30484                 }
30485
30486                 if(this.labelmd > 0){
30487                     labelCls = ' col-md-' + this.labelmd;
30488                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30489                 }
30490
30491                 if(this.labelsm > 0){
30492                     labelCls = ' col-sm-' + this.labelsm;
30493                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30494                 }
30495
30496                 if(this.labelxs > 0){
30497                     labelCls = ' col-xs-' + this.labelxs;
30498                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30499                 }
30500             }
30501             
30502             label.cls += ' ' + labelCls;
30503             
30504             cfg.cn.push(label);
30505         }
30506         
30507         Roo.each(['day', 'month', 'year'], function(t){
30508             cfg.cn.push({
30509                 tag : 'div',
30510                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30511             });
30512         }, this);
30513         
30514         return cfg;
30515     },
30516     
30517     inputEl: function ()
30518     {
30519         return this.el.select('.roo-date-split-field-group-value', true).first();
30520     },
30521     
30522     onRender : function(ct, position) 
30523     {
30524         var _this = this;
30525         
30526         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30527         
30528         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30529         
30530         this.dayField = new Roo.bootstrap.ComboBox({
30531             allowBlank : this.dayAllowBlank,
30532             alwaysQuery : true,
30533             displayField : 'value',
30534             editable : false,
30535             fieldLabel : '',
30536             forceSelection : true,
30537             mode : 'local',
30538             placeholder : this.dayPlaceholder,
30539             selectOnFocus : true,
30540             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30541             triggerAction : 'all',
30542             typeAhead : true,
30543             valueField : 'value',
30544             store : new Roo.data.SimpleStore({
30545                 data : (function() {    
30546                     var days = [];
30547                     _this.fireEvent('days', _this, days);
30548                     return days;
30549                 })(),
30550                 fields : [ 'value' ]
30551             }),
30552             listeners : {
30553                 select : function (_self, record, index)
30554                 {
30555                     _this.setValue(_this.getValue());
30556                 }
30557             }
30558         });
30559
30560         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30561         
30562         this.monthField = new Roo.bootstrap.MonthField({
30563             after : '<i class=\"fa fa-calendar\"></i>',
30564             allowBlank : this.monthAllowBlank,
30565             placeholder : this.monthPlaceholder,
30566             readOnly : true,
30567             listeners : {
30568                 render : function (_self)
30569                 {
30570                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30571                         e.preventDefault();
30572                         _self.focus();
30573                     });
30574                 },
30575                 select : function (_self, oldvalue, newvalue)
30576                 {
30577                     _this.setValue(_this.getValue());
30578                 }
30579             }
30580         });
30581         
30582         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30583         
30584         this.yearField = new Roo.bootstrap.ComboBox({
30585             allowBlank : this.yearAllowBlank,
30586             alwaysQuery : true,
30587             displayField : 'value',
30588             editable : false,
30589             fieldLabel : '',
30590             forceSelection : true,
30591             mode : 'local',
30592             placeholder : this.yearPlaceholder,
30593             selectOnFocus : true,
30594             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30595             triggerAction : 'all',
30596             typeAhead : true,
30597             valueField : 'value',
30598             store : new Roo.data.SimpleStore({
30599                 data : (function() {
30600                     var years = [];
30601                     _this.fireEvent('years', _this, years);
30602                     return years;
30603                 })(),
30604                 fields : [ 'value' ]
30605             }),
30606             listeners : {
30607                 select : function (_self, record, index)
30608                 {
30609                     _this.setValue(_this.getValue());
30610                 }
30611             }
30612         });
30613
30614         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30615     },
30616     
30617     setValue : function(v, format)
30618     {
30619         this.inputEl.dom.value = v;
30620         
30621         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30622         
30623         var d = Date.parseDate(v, f);
30624         
30625         if(!d){
30626             this.validate();
30627             return;
30628         }
30629         
30630         this.setDay(d.format(this.dayFormat));
30631         this.setMonth(d.format(this.monthFormat));
30632         this.setYear(d.format(this.yearFormat));
30633         
30634         this.validate();
30635         
30636         return;
30637     },
30638     
30639     setDay : function(v)
30640     {
30641         this.dayField.setValue(v);
30642         this.inputEl.dom.value = this.getValue();
30643         this.validate();
30644         return;
30645     },
30646     
30647     setMonth : function(v)
30648     {
30649         this.monthField.setValue(v, true);
30650         this.inputEl.dom.value = this.getValue();
30651         this.validate();
30652         return;
30653     },
30654     
30655     setYear : function(v)
30656     {
30657         this.yearField.setValue(v);
30658         this.inputEl.dom.value = this.getValue();
30659         this.validate();
30660         return;
30661     },
30662     
30663     getDay : function()
30664     {
30665         return this.dayField.getValue();
30666     },
30667     
30668     getMonth : function()
30669     {
30670         return this.monthField.getValue();
30671     },
30672     
30673     getYear : function()
30674     {
30675         return this.yearField.getValue();
30676     },
30677     
30678     getValue : function()
30679     {
30680         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30681         
30682         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30683         
30684         return date;
30685     },
30686     
30687     reset : function()
30688     {
30689         this.setDay('');
30690         this.setMonth('');
30691         this.setYear('');
30692         this.inputEl.dom.value = '';
30693         this.validate();
30694         return;
30695     },
30696     
30697     validate : function()
30698     {
30699         var d = this.dayField.validate();
30700         var m = this.monthField.validate();
30701         var y = this.yearField.validate();
30702         
30703         var valid = true;
30704         
30705         if(
30706                 (!this.dayAllowBlank && !d) ||
30707                 (!this.monthAllowBlank && !m) ||
30708                 (!this.yearAllowBlank && !y)
30709         ){
30710             valid = false;
30711         }
30712         
30713         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30714             return valid;
30715         }
30716         
30717         if(valid){
30718             this.markValid();
30719             return valid;
30720         }
30721         
30722         this.markInvalid();
30723         
30724         return valid;
30725     },
30726     
30727     markValid : function()
30728     {
30729         
30730         var label = this.el.select('label', true).first();
30731         var icon = this.el.select('i.fa-star', true).first();
30732
30733         if(label && icon){
30734             icon.remove();
30735         }
30736         
30737         this.fireEvent('valid', this);
30738     },
30739     
30740      /**
30741      * Mark this field as invalid
30742      * @param {String} msg The validation message
30743      */
30744     markInvalid : function(msg)
30745     {
30746         
30747         var label = this.el.select('label', true).first();
30748         var icon = this.el.select('i.fa-star', true).first();
30749
30750         if(label && !icon){
30751             this.el.select('.roo-date-split-field-label', true).createChild({
30752                 tag : 'i',
30753                 cls : 'text-danger fa fa-lg fa-star',
30754                 tooltip : 'This field is required',
30755                 style : 'margin-right:5px;'
30756             }, label, true);
30757         }
30758         
30759         this.fireEvent('invalid', this, msg);
30760     },
30761     
30762     clearInvalid : function()
30763     {
30764         var label = this.el.select('label', true).first();
30765         var icon = this.el.select('i.fa-star', true).first();
30766
30767         if(label && icon){
30768             icon.remove();
30769         }
30770         
30771         this.fireEvent('valid', this);
30772     },
30773     
30774     getName: function()
30775     {
30776         return this.name;
30777     }
30778     
30779 });
30780
30781  /**
30782  *
30783  * This is based on 
30784  * http://masonry.desandro.com
30785  *
30786  * The idea is to render all the bricks based on vertical width...
30787  *
30788  * The original code extends 'outlayer' - we might need to use that....
30789  * 
30790  */
30791
30792
30793 /**
30794  * @class Roo.bootstrap.LayoutMasonry
30795  * @extends Roo.bootstrap.Component
30796  * Bootstrap Layout Masonry class
30797  * 
30798  * @constructor
30799  * Create a new Element
30800  * @param {Object} config The config object
30801  */
30802
30803 Roo.bootstrap.LayoutMasonry = function(config){
30804     
30805     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30806     
30807     this.bricks = [];
30808     
30809     Roo.bootstrap.LayoutMasonry.register(this);
30810     
30811     this.addEvents({
30812         // raw events
30813         /**
30814          * @event layout
30815          * Fire after layout the items
30816          * @param {Roo.bootstrap.LayoutMasonry} this
30817          * @param {Roo.EventObject} e
30818          */
30819         "layout" : true
30820     });
30821     
30822 };
30823
30824 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30825     
30826     /**
30827      * @cfg {Boolean} isLayoutInstant = no animation?
30828      */   
30829     isLayoutInstant : false, // needed?
30830    
30831     /**
30832      * @cfg {Number} boxWidth  width of the columns
30833      */   
30834     boxWidth : 450,
30835     
30836       /**
30837      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30838      */   
30839     boxHeight : 0,
30840     
30841     /**
30842      * @cfg {Number} padWidth padding below box..
30843      */   
30844     padWidth : 10, 
30845     
30846     /**
30847      * @cfg {Number} gutter gutter width..
30848      */   
30849     gutter : 10,
30850     
30851      /**
30852      * @cfg {Number} maxCols maximum number of columns
30853      */   
30854     
30855     maxCols: 0,
30856     
30857     /**
30858      * @cfg {Boolean} isAutoInitial defalut true
30859      */   
30860     isAutoInitial : true, 
30861     
30862     containerWidth: 0,
30863     
30864     /**
30865      * @cfg {Boolean} isHorizontal defalut false
30866      */   
30867     isHorizontal : false, 
30868
30869     currentSize : null,
30870     
30871     tag: 'div',
30872     
30873     cls: '',
30874     
30875     bricks: null, //CompositeElement
30876     
30877     cols : 1,
30878     
30879     _isLayoutInited : false,
30880     
30881 //    isAlternative : false, // only use for vertical layout...
30882     
30883     /**
30884      * @cfg {Number} alternativePadWidth padding below box..
30885      */   
30886     alternativePadWidth : 50,
30887     
30888     selectedBrick : [],
30889     
30890     getAutoCreate : function(){
30891         
30892         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30893         
30894         var cfg = {
30895             tag: this.tag,
30896             cls: 'blog-masonary-wrapper ' + this.cls,
30897             cn : {
30898                 cls : 'mas-boxes masonary'
30899             }
30900         };
30901         
30902         return cfg;
30903     },
30904     
30905     getChildContainer: function( )
30906     {
30907         if (this.boxesEl) {
30908             return this.boxesEl;
30909         }
30910         
30911         this.boxesEl = this.el.select('.mas-boxes').first();
30912         
30913         return this.boxesEl;
30914     },
30915     
30916     
30917     initEvents : function()
30918     {
30919         var _this = this;
30920         
30921         if(this.isAutoInitial){
30922             Roo.log('hook children rendered');
30923             this.on('childrenrendered', function() {
30924                 Roo.log('children rendered');
30925                 _this.initial();
30926             } ,this);
30927         }
30928     },
30929     
30930     initial : function()
30931     {
30932         this.selectedBrick = [];
30933         
30934         this.currentSize = this.el.getBox(true);
30935         
30936         Roo.EventManager.onWindowResize(this.resize, this); 
30937
30938         if(!this.isAutoInitial){
30939             this.layout();
30940             return;
30941         }
30942         
30943         this.layout();
30944         
30945         return;
30946         //this.layout.defer(500,this);
30947         
30948     },
30949     
30950     resize : function()
30951     {
30952         var cs = this.el.getBox(true);
30953         
30954         if (
30955                 this.currentSize.width == cs.width && 
30956                 this.currentSize.x == cs.x && 
30957                 this.currentSize.height == cs.height && 
30958                 this.currentSize.y == cs.y 
30959         ) {
30960             Roo.log("no change in with or X or Y");
30961             return;
30962         }
30963         
30964         this.currentSize = cs;
30965         
30966         this.layout();
30967         
30968     },
30969     
30970     layout : function()
30971     {   
30972         this._resetLayout();
30973         
30974         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30975         
30976         this.layoutItems( isInstant );
30977       
30978         this._isLayoutInited = true;
30979         
30980         this.fireEvent('layout', this);
30981         
30982     },
30983     
30984     _resetLayout : function()
30985     {
30986         if(this.isHorizontal){
30987             this.horizontalMeasureColumns();
30988             return;
30989         }
30990         
30991         this.verticalMeasureColumns();
30992         
30993     },
30994     
30995     verticalMeasureColumns : function()
30996     {
30997         this.getContainerWidth();
30998         
30999 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31000 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31001 //            return;
31002 //        }
31003         
31004         var boxWidth = this.boxWidth + this.padWidth;
31005         
31006         if(this.containerWidth < this.boxWidth){
31007             boxWidth = this.containerWidth
31008         }
31009         
31010         var containerWidth = this.containerWidth;
31011         
31012         var cols = Math.floor(containerWidth / boxWidth);
31013         
31014         this.cols = Math.max( cols, 1 );
31015         
31016         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31017         
31018         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31019         
31020         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31021         
31022         this.colWidth = boxWidth + avail - this.padWidth;
31023         
31024         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31025         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31026     },
31027     
31028     horizontalMeasureColumns : function()
31029     {
31030         this.getContainerWidth();
31031         
31032         var boxWidth = this.boxWidth;
31033         
31034         if(this.containerWidth < boxWidth){
31035             boxWidth = this.containerWidth;
31036         }
31037         
31038         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31039         
31040         this.el.setHeight(boxWidth);
31041         
31042     },
31043     
31044     getContainerWidth : function()
31045     {
31046         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31047     },
31048     
31049     layoutItems : function( isInstant )
31050     {
31051         Roo.log(this.bricks);
31052         
31053         var items = Roo.apply([], this.bricks);
31054         
31055         if(this.isHorizontal){
31056             this._horizontalLayoutItems( items , isInstant );
31057             return;
31058         }
31059         
31060 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31061 //            this._verticalAlternativeLayoutItems( items , isInstant );
31062 //            return;
31063 //        }
31064         
31065         this._verticalLayoutItems( items , isInstant );
31066         
31067     },
31068     
31069     _verticalLayoutItems : function ( items , isInstant)
31070     {
31071         if ( !items || !items.length ) {
31072             return;
31073         }
31074         
31075         var standard = [
31076             ['xs', 'xs', 'xs', 'tall'],
31077             ['xs', 'xs', 'tall'],
31078             ['xs', 'xs', 'sm'],
31079             ['xs', 'xs', 'xs'],
31080             ['xs', 'tall'],
31081             ['xs', 'sm'],
31082             ['xs', 'xs'],
31083             ['xs'],
31084             
31085             ['sm', 'xs', 'xs'],
31086             ['sm', 'xs'],
31087             ['sm'],
31088             
31089             ['tall', 'xs', 'xs', 'xs'],
31090             ['tall', 'xs', 'xs'],
31091             ['tall', 'xs'],
31092             ['tall']
31093             
31094         ];
31095         
31096         var queue = [];
31097         
31098         var boxes = [];
31099         
31100         var box = [];
31101         
31102         Roo.each(items, function(item, k){
31103             
31104             switch (item.size) {
31105                 // these layouts take up a full box,
31106                 case 'md' :
31107                 case 'md-left' :
31108                 case 'md-right' :
31109                 case 'wide' :
31110                     
31111                     if(box.length){
31112                         boxes.push(box);
31113                         box = [];
31114                     }
31115                     
31116                     boxes.push([item]);
31117                     
31118                     break;
31119                     
31120                 case 'xs' :
31121                 case 'sm' :
31122                 case 'tall' :
31123                     
31124                     box.push(item);
31125                     
31126                     break;
31127                 default :
31128                     break;
31129                     
31130             }
31131             
31132         }, this);
31133         
31134         if(box.length){
31135             boxes.push(box);
31136             box = [];
31137         }
31138         
31139         var filterPattern = function(box, length)
31140         {
31141             if(!box.length){
31142                 return;
31143             }
31144             
31145             var match = false;
31146             
31147             var pattern = box.slice(0, length);
31148             
31149             var format = [];
31150             
31151             Roo.each(pattern, function(i){
31152                 format.push(i.size);
31153             }, this);
31154             
31155             Roo.each(standard, function(s){
31156                 
31157                 if(String(s) != String(format)){
31158                     return;
31159                 }
31160                 
31161                 match = true;
31162                 return false;
31163                 
31164             }, this);
31165             
31166             if(!match && length == 1){
31167                 return;
31168             }
31169             
31170             if(!match){
31171                 filterPattern(box, length - 1);
31172                 return;
31173             }
31174                 
31175             queue.push(pattern);
31176
31177             box = box.slice(length, box.length);
31178
31179             filterPattern(box, 4);
31180
31181             return;
31182             
31183         }
31184         
31185         Roo.each(boxes, function(box, k){
31186             
31187             if(!box.length){
31188                 return;
31189             }
31190             
31191             if(box.length == 1){
31192                 queue.push(box);
31193                 return;
31194             }
31195             
31196             filterPattern(box, 4);
31197             
31198         }, this);
31199         
31200         this._processVerticalLayoutQueue( queue, isInstant );
31201         
31202     },
31203     
31204 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31205 //    {
31206 //        if ( !items || !items.length ) {
31207 //            return;
31208 //        }
31209 //
31210 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31211 //        
31212 //    },
31213     
31214     _horizontalLayoutItems : function ( items , isInstant)
31215     {
31216         if ( !items || !items.length || items.length < 3) {
31217             return;
31218         }
31219         
31220         items.reverse();
31221         
31222         var eItems = items.slice(0, 3);
31223         
31224         items = items.slice(3, items.length);
31225         
31226         var standard = [
31227             ['xs', 'xs', 'xs', 'wide'],
31228             ['xs', 'xs', 'wide'],
31229             ['xs', 'xs', 'sm'],
31230             ['xs', 'xs', 'xs'],
31231             ['xs', 'wide'],
31232             ['xs', 'sm'],
31233             ['xs', 'xs'],
31234             ['xs'],
31235             
31236             ['sm', 'xs', 'xs'],
31237             ['sm', 'xs'],
31238             ['sm'],
31239             
31240             ['wide', 'xs', 'xs', 'xs'],
31241             ['wide', 'xs', 'xs'],
31242             ['wide', 'xs'],
31243             ['wide'],
31244             
31245             ['wide-thin']
31246         ];
31247         
31248         var queue = [];
31249         
31250         var boxes = [];
31251         
31252         var box = [];
31253         
31254         Roo.each(items, function(item, k){
31255             
31256             switch (item.size) {
31257                 case 'md' :
31258                 case 'md-left' :
31259                 case 'md-right' :
31260                 case 'tall' :
31261                     
31262                     if(box.length){
31263                         boxes.push(box);
31264                         box = [];
31265                     }
31266                     
31267                     boxes.push([item]);
31268                     
31269                     break;
31270                     
31271                 case 'xs' :
31272                 case 'sm' :
31273                 case 'wide' :
31274                 case 'wide-thin' :
31275                     
31276                     box.push(item);
31277                     
31278                     break;
31279                 default :
31280                     break;
31281                     
31282             }
31283             
31284         }, this);
31285         
31286         if(box.length){
31287             boxes.push(box);
31288             box = [];
31289         }
31290         
31291         var filterPattern = function(box, length)
31292         {
31293             if(!box.length){
31294                 return;
31295             }
31296             
31297             var match = false;
31298             
31299             var pattern = box.slice(0, length);
31300             
31301             var format = [];
31302             
31303             Roo.each(pattern, function(i){
31304                 format.push(i.size);
31305             }, this);
31306             
31307             Roo.each(standard, function(s){
31308                 
31309                 if(String(s) != String(format)){
31310                     return;
31311                 }
31312                 
31313                 match = true;
31314                 return false;
31315                 
31316             }, this);
31317             
31318             if(!match && length == 1){
31319                 return;
31320             }
31321             
31322             if(!match){
31323                 filterPattern(box, length - 1);
31324                 return;
31325             }
31326                 
31327             queue.push(pattern);
31328
31329             box = box.slice(length, box.length);
31330
31331             filterPattern(box, 4);
31332
31333             return;
31334             
31335         }
31336         
31337         Roo.each(boxes, function(box, k){
31338             
31339             if(!box.length){
31340                 return;
31341             }
31342             
31343             if(box.length == 1){
31344                 queue.push(box);
31345                 return;
31346             }
31347             
31348             filterPattern(box, 4);
31349             
31350         }, this);
31351         
31352         
31353         var prune = [];
31354         
31355         var pos = this.el.getBox(true);
31356         
31357         var minX = pos.x;
31358         
31359         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31360         
31361         var hit_end = false;
31362         
31363         Roo.each(queue, function(box){
31364             
31365             if(hit_end){
31366                 
31367                 Roo.each(box, function(b){
31368                 
31369                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31370                     b.el.hide();
31371
31372                 }, this);
31373
31374                 return;
31375             }
31376             
31377             var mx = 0;
31378             
31379             Roo.each(box, function(b){
31380                 
31381                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31382                 b.el.show();
31383
31384                 mx = Math.max(mx, b.x);
31385                 
31386             }, this);
31387             
31388             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31389             
31390             if(maxX < minX){
31391                 
31392                 Roo.each(box, function(b){
31393                 
31394                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31395                     b.el.hide();
31396                     
31397                 }, this);
31398                 
31399                 hit_end = true;
31400                 
31401                 return;
31402             }
31403             
31404             prune.push(box);
31405             
31406         }, this);
31407         
31408         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31409     },
31410     
31411     /** Sets position of item in DOM
31412     * @param {Element} item
31413     * @param {Number} x - horizontal position
31414     * @param {Number} y - vertical position
31415     * @param {Boolean} isInstant - disables transitions
31416     */
31417     _processVerticalLayoutQueue : function( queue, isInstant )
31418     {
31419         var pos = this.el.getBox(true);
31420         var x = pos.x;
31421         var y = pos.y;
31422         var maxY = [];
31423         
31424         for (var i = 0; i < this.cols; i++){
31425             maxY[i] = pos.y;
31426         }
31427         
31428         Roo.each(queue, function(box, k){
31429             
31430             var col = k % this.cols;
31431             
31432             Roo.each(box, function(b,kk){
31433                 
31434                 b.el.position('absolute');
31435                 
31436                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31437                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31438                 
31439                 if(b.size == 'md-left' || b.size == 'md-right'){
31440                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31441                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31442                 }
31443                 
31444                 b.el.setWidth(width);
31445                 b.el.setHeight(height);
31446                 // iframe?
31447                 b.el.select('iframe',true).setSize(width,height);
31448                 
31449             }, this);
31450             
31451             for (var i = 0; i < this.cols; i++){
31452                 
31453                 if(maxY[i] < maxY[col]){
31454                     col = i;
31455                     continue;
31456                 }
31457                 
31458                 col = Math.min(col, i);
31459                 
31460             }
31461             
31462             x = pos.x + col * (this.colWidth + this.padWidth);
31463             
31464             y = maxY[col];
31465             
31466             var positions = [];
31467             
31468             switch (box.length){
31469                 case 1 :
31470                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31471                     break;
31472                 case 2 :
31473                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31474                     break;
31475                 case 3 :
31476                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31477                     break;
31478                 case 4 :
31479                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31480                     break;
31481                 default :
31482                     break;
31483             }
31484             
31485             Roo.each(box, function(b,kk){
31486                 
31487                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31488                 
31489                 var sz = b.el.getSize();
31490                 
31491                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31492                 
31493             }, this);
31494             
31495         }, this);
31496         
31497         var mY = 0;
31498         
31499         for (var i = 0; i < this.cols; i++){
31500             mY = Math.max(mY, maxY[i]);
31501         }
31502         
31503         this.el.setHeight(mY - pos.y);
31504         
31505     },
31506     
31507 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31508 //    {
31509 //        var pos = this.el.getBox(true);
31510 //        var x = pos.x;
31511 //        var y = pos.y;
31512 //        var maxX = pos.right;
31513 //        
31514 //        var maxHeight = 0;
31515 //        
31516 //        Roo.each(items, function(item, k){
31517 //            
31518 //            var c = k % 2;
31519 //            
31520 //            item.el.position('absolute');
31521 //                
31522 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31523 //
31524 //            item.el.setWidth(width);
31525 //
31526 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31527 //
31528 //            item.el.setHeight(height);
31529 //            
31530 //            if(c == 0){
31531 //                item.el.setXY([x, y], isInstant ? false : true);
31532 //            } else {
31533 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31534 //            }
31535 //            
31536 //            y = y + height + this.alternativePadWidth;
31537 //            
31538 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31539 //            
31540 //        }, this);
31541 //        
31542 //        this.el.setHeight(maxHeight);
31543 //        
31544 //    },
31545     
31546     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31547     {
31548         var pos = this.el.getBox(true);
31549         
31550         var minX = pos.x;
31551         var minY = pos.y;
31552         
31553         var maxX = pos.right;
31554         
31555         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31556         
31557         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31558         
31559         Roo.each(queue, function(box, k){
31560             
31561             Roo.each(box, function(b, kk){
31562                 
31563                 b.el.position('absolute');
31564                 
31565                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31566                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31567                 
31568                 if(b.size == 'md-left' || b.size == 'md-right'){
31569                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31570                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31571                 }
31572                 
31573                 b.el.setWidth(width);
31574                 b.el.setHeight(height);
31575                 
31576             }, this);
31577             
31578             if(!box.length){
31579                 return;
31580             }
31581             
31582             var positions = [];
31583             
31584             switch (box.length){
31585                 case 1 :
31586                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31587                     break;
31588                 case 2 :
31589                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31590                     break;
31591                 case 3 :
31592                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31593                     break;
31594                 case 4 :
31595                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31596                     break;
31597                 default :
31598                     break;
31599             }
31600             
31601             Roo.each(box, function(b,kk){
31602                 
31603                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31604                 
31605                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31606                 
31607             }, this);
31608             
31609         }, this);
31610         
31611     },
31612     
31613     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31614     {
31615         Roo.each(eItems, function(b,k){
31616             
31617             b.size = (k == 0) ? 'sm' : 'xs';
31618             b.x = (k == 0) ? 2 : 1;
31619             b.y = (k == 0) ? 2 : 1;
31620             
31621             b.el.position('absolute');
31622             
31623             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31624                 
31625             b.el.setWidth(width);
31626             
31627             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31628             
31629             b.el.setHeight(height);
31630             
31631         }, this);
31632
31633         var positions = [];
31634         
31635         positions.push({
31636             x : maxX - this.unitWidth * 2 - this.gutter,
31637             y : minY
31638         });
31639         
31640         positions.push({
31641             x : maxX - this.unitWidth,
31642             y : minY + (this.unitWidth + this.gutter) * 2
31643         });
31644         
31645         positions.push({
31646             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31647             y : minY
31648         });
31649         
31650         Roo.each(eItems, function(b,k){
31651             
31652             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31653
31654         }, this);
31655         
31656     },
31657     
31658     getVerticalOneBoxColPositions : function(x, y, box)
31659     {
31660         var pos = [];
31661         
31662         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31663         
31664         if(box[0].size == 'md-left'){
31665             rand = 0;
31666         }
31667         
31668         if(box[0].size == 'md-right'){
31669             rand = 1;
31670         }
31671         
31672         pos.push({
31673             x : x + (this.unitWidth + this.gutter) * rand,
31674             y : y
31675         });
31676         
31677         return pos;
31678     },
31679     
31680     getVerticalTwoBoxColPositions : function(x, y, box)
31681     {
31682         var pos = [];
31683         
31684         if(box[0].size == 'xs'){
31685             
31686             pos.push({
31687                 x : x,
31688                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31689             });
31690
31691             pos.push({
31692                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31693                 y : y
31694             });
31695             
31696             return pos;
31697             
31698         }
31699         
31700         pos.push({
31701             x : x,
31702             y : y
31703         });
31704
31705         pos.push({
31706             x : x + (this.unitWidth + this.gutter) * 2,
31707             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31708         });
31709         
31710         return pos;
31711         
31712     },
31713     
31714     getVerticalThreeBoxColPositions : function(x, y, box)
31715     {
31716         var pos = [];
31717         
31718         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31719             
31720             pos.push({
31721                 x : x,
31722                 y : y
31723             });
31724
31725             pos.push({
31726                 x : x + (this.unitWidth + this.gutter) * 1,
31727                 y : y
31728             });
31729             
31730             pos.push({
31731                 x : x + (this.unitWidth + this.gutter) * 2,
31732                 y : y
31733             });
31734             
31735             return pos;
31736             
31737         }
31738         
31739         if(box[0].size == 'xs' && box[1].size == 'xs'){
31740             
31741             pos.push({
31742                 x : x,
31743                 y : y
31744             });
31745
31746             pos.push({
31747                 x : x,
31748                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31749             });
31750             
31751             pos.push({
31752                 x : x + (this.unitWidth + this.gutter) * 1,
31753                 y : y
31754             });
31755             
31756             return pos;
31757             
31758         }
31759         
31760         pos.push({
31761             x : x,
31762             y : y
31763         });
31764
31765         pos.push({
31766             x : x + (this.unitWidth + this.gutter) * 2,
31767             y : y
31768         });
31769
31770         pos.push({
31771             x : x + (this.unitWidth + this.gutter) * 2,
31772             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31773         });
31774             
31775         return pos;
31776         
31777     },
31778     
31779     getVerticalFourBoxColPositions : function(x, y, box)
31780     {
31781         var pos = [];
31782         
31783         if(box[0].size == 'xs'){
31784             
31785             pos.push({
31786                 x : x,
31787                 y : y
31788             });
31789
31790             pos.push({
31791                 x : x,
31792                 y : y + (this.unitHeight + this.gutter) * 1
31793             });
31794             
31795             pos.push({
31796                 x : x,
31797                 y : y + (this.unitHeight + this.gutter) * 2
31798             });
31799             
31800             pos.push({
31801                 x : x + (this.unitWidth + this.gutter) * 1,
31802                 y : y
31803             });
31804             
31805             return pos;
31806             
31807         }
31808         
31809         pos.push({
31810             x : x,
31811             y : y
31812         });
31813
31814         pos.push({
31815             x : x + (this.unitWidth + this.gutter) * 2,
31816             y : y
31817         });
31818
31819         pos.push({
31820             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31821             y : y + (this.unitHeight + this.gutter) * 1
31822         });
31823
31824         pos.push({
31825             x : x + (this.unitWidth + this.gutter) * 2,
31826             y : y + (this.unitWidth + this.gutter) * 2
31827         });
31828
31829         return pos;
31830         
31831     },
31832     
31833     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31834     {
31835         var pos = [];
31836         
31837         if(box[0].size == 'md-left'){
31838             pos.push({
31839                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31840                 y : minY
31841             });
31842             
31843             return pos;
31844         }
31845         
31846         if(box[0].size == 'md-right'){
31847             pos.push({
31848                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31849                 y : minY + (this.unitWidth + this.gutter) * 1
31850             });
31851             
31852             return pos;
31853         }
31854         
31855         var rand = Math.floor(Math.random() * (4 - box[0].y));
31856         
31857         pos.push({
31858             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31859             y : minY + (this.unitWidth + this.gutter) * rand
31860         });
31861         
31862         return pos;
31863         
31864     },
31865     
31866     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31867     {
31868         var pos = [];
31869         
31870         if(box[0].size == 'xs'){
31871             
31872             pos.push({
31873                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31874                 y : minY
31875             });
31876
31877             pos.push({
31878                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31879                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31880             });
31881             
31882             return pos;
31883             
31884         }
31885         
31886         pos.push({
31887             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31888             y : minY
31889         });
31890
31891         pos.push({
31892             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31893             y : minY + (this.unitWidth + this.gutter) * 2
31894         });
31895         
31896         return pos;
31897         
31898     },
31899     
31900     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31901     {
31902         var pos = [];
31903         
31904         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31905             
31906             pos.push({
31907                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31908                 y : minY
31909             });
31910
31911             pos.push({
31912                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31913                 y : minY + (this.unitWidth + this.gutter) * 1
31914             });
31915             
31916             pos.push({
31917                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31918                 y : minY + (this.unitWidth + this.gutter) * 2
31919             });
31920             
31921             return pos;
31922             
31923         }
31924         
31925         if(box[0].size == 'xs' && box[1].size == 'xs'){
31926             
31927             pos.push({
31928                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31929                 y : minY
31930             });
31931
31932             pos.push({
31933                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31934                 y : minY
31935             });
31936             
31937             pos.push({
31938                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31939                 y : minY + (this.unitWidth + this.gutter) * 1
31940             });
31941             
31942             return pos;
31943             
31944         }
31945         
31946         pos.push({
31947             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31948             y : minY
31949         });
31950
31951         pos.push({
31952             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31953             y : minY + (this.unitWidth + this.gutter) * 2
31954         });
31955
31956         pos.push({
31957             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31958             y : minY + (this.unitWidth + this.gutter) * 2
31959         });
31960             
31961         return pos;
31962         
31963     },
31964     
31965     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31966     {
31967         var pos = [];
31968         
31969         if(box[0].size == 'xs'){
31970             
31971             pos.push({
31972                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31973                 y : minY
31974             });
31975
31976             pos.push({
31977                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31978                 y : minY
31979             });
31980             
31981             pos.push({
31982                 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),
31983                 y : minY
31984             });
31985             
31986             pos.push({
31987                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31988                 y : minY + (this.unitWidth + this.gutter) * 1
31989             });
31990             
31991             return pos;
31992             
31993         }
31994         
31995         pos.push({
31996             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31997             y : minY
31998         });
31999         
32000         pos.push({
32001             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002             y : minY + (this.unitWidth + this.gutter) * 2
32003         });
32004         
32005         pos.push({
32006             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32007             y : minY + (this.unitWidth + this.gutter) * 2
32008         });
32009         
32010         pos.push({
32011             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),
32012             y : minY + (this.unitWidth + this.gutter) * 2
32013         });
32014
32015         return pos;
32016         
32017     },
32018     
32019     /**
32020     * remove a Masonry Brick
32021     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32022     */
32023     removeBrick : function(brick_id)
32024     {
32025         if (!brick_id) {
32026             return;
32027         }
32028         
32029         for (var i = 0; i<this.bricks.length; i++) {
32030             if (this.bricks[i].id == brick_id) {
32031                 this.bricks.splice(i,1);
32032                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32033                 this.initial();
32034             }
32035         }
32036     },
32037     
32038     /**
32039     * adds a Masonry Brick
32040     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32041     */
32042     addBrick : function(cfg)
32043     {
32044         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32045         //this.register(cn);
32046         cn.parentId = this.id;
32047         cn.render(this.el);
32048         return cn;
32049     },
32050     
32051     /**
32052     * register a Masonry Brick
32053     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32054     */
32055     
32056     register : function(brick)
32057     {
32058         this.bricks.push(brick);
32059         brick.masonryId = this.id;
32060     },
32061     
32062     /**
32063     * clear all the Masonry Brick
32064     */
32065     clearAll : function()
32066     {
32067         this.bricks = [];
32068         //this.getChildContainer().dom.innerHTML = "";
32069         this.el.dom.innerHTML = '';
32070     },
32071     
32072     getSelected : function()
32073     {
32074         if (!this.selectedBrick) {
32075             return false;
32076         }
32077         
32078         return this.selectedBrick;
32079     }
32080 });
32081
32082 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32083     
32084     groups: {},
32085      /**
32086     * register a Masonry Layout
32087     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32088     */
32089     
32090     register : function(layout)
32091     {
32092         this.groups[layout.id] = layout;
32093     },
32094     /**
32095     * fetch a  Masonry Layout based on the masonry layout ID
32096     * @param {string} the masonry layout to add
32097     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32098     */
32099     
32100     get: function(layout_id) {
32101         if (typeof(this.groups[layout_id]) == 'undefined') {
32102             return false;
32103         }
32104         return this.groups[layout_id] ;
32105     }
32106     
32107     
32108     
32109 });
32110
32111  
32112
32113  /**
32114  *
32115  * This is based on 
32116  * http://masonry.desandro.com
32117  *
32118  * The idea is to render all the bricks based on vertical width...
32119  *
32120  * The original code extends 'outlayer' - we might need to use that....
32121  * 
32122  */
32123
32124
32125 /**
32126  * @class Roo.bootstrap.LayoutMasonryAuto
32127  * @extends Roo.bootstrap.Component
32128  * Bootstrap Layout Masonry class
32129  * 
32130  * @constructor
32131  * Create a new Element
32132  * @param {Object} config The config object
32133  */
32134
32135 Roo.bootstrap.LayoutMasonryAuto = function(config){
32136     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32137 };
32138
32139 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32140     
32141       /**
32142      * @cfg {Boolean} isFitWidth  - resize the width..
32143      */   
32144     isFitWidth : false,  // options..
32145     /**
32146      * @cfg {Boolean} isOriginLeft = left align?
32147      */   
32148     isOriginLeft : true,
32149     /**
32150      * @cfg {Boolean} isOriginTop = top align?
32151      */   
32152     isOriginTop : false,
32153     /**
32154      * @cfg {Boolean} isLayoutInstant = no animation?
32155      */   
32156     isLayoutInstant : false, // needed?
32157     /**
32158      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32159      */   
32160     isResizingContainer : true,
32161     /**
32162      * @cfg {Number} columnWidth  width of the columns 
32163      */   
32164     
32165     columnWidth : 0,
32166     
32167     /**
32168      * @cfg {Number} maxCols maximum number of columns
32169      */   
32170     
32171     maxCols: 0,
32172     /**
32173      * @cfg {Number} padHeight padding below box..
32174      */   
32175     
32176     padHeight : 10, 
32177     
32178     /**
32179      * @cfg {Boolean} isAutoInitial defalut true
32180      */   
32181     
32182     isAutoInitial : true, 
32183     
32184     // private?
32185     gutter : 0,
32186     
32187     containerWidth: 0,
32188     initialColumnWidth : 0,
32189     currentSize : null,
32190     
32191     colYs : null, // array.
32192     maxY : 0,
32193     padWidth: 10,
32194     
32195     
32196     tag: 'div',
32197     cls: '',
32198     bricks: null, //CompositeElement
32199     cols : 0, // array?
32200     // element : null, // wrapped now this.el
32201     _isLayoutInited : null, 
32202     
32203     
32204     getAutoCreate : function(){
32205         
32206         var cfg = {
32207             tag: this.tag,
32208             cls: 'blog-masonary-wrapper ' + this.cls,
32209             cn : {
32210                 cls : 'mas-boxes masonary'
32211             }
32212         };
32213         
32214         return cfg;
32215     },
32216     
32217     getChildContainer: function( )
32218     {
32219         if (this.boxesEl) {
32220             return this.boxesEl;
32221         }
32222         
32223         this.boxesEl = this.el.select('.mas-boxes').first();
32224         
32225         return this.boxesEl;
32226     },
32227     
32228     
32229     initEvents : function()
32230     {
32231         var _this = this;
32232         
32233         if(this.isAutoInitial){
32234             Roo.log('hook children rendered');
32235             this.on('childrenrendered', function() {
32236                 Roo.log('children rendered');
32237                 _this.initial();
32238             } ,this);
32239         }
32240         
32241     },
32242     
32243     initial : function()
32244     {
32245         this.reloadItems();
32246
32247         this.currentSize = this.el.getBox(true);
32248
32249         /// was window resize... - let's see if this works..
32250         Roo.EventManager.onWindowResize(this.resize, this); 
32251
32252         if(!this.isAutoInitial){
32253             this.layout();
32254             return;
32255         }
32256         
32257         this.layout.defer(500,this);
32258     },
32259     
32260     reloadItems: function()
32261     {
32262         this.bricks = this.el.select('.masonry-brick', true);
32263         
32264         this.bricks.each(function(b) {
32265             //Roo.log(b.getSize());
32266             if (!b.attr('originalwidth')) {
32267                 b.attr('originalwidth',  b.getSize().width);
32268             }
32269             
32270         });
32271         
32272         Roo.log(this.bricks.elements.length);
32273     },
32274     
32275     resize : function()
32276     {
32277         Roo.log('resize');
32278         var cs = this.el.getBox(true);
32279         
32280         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32281             Roo.log("no change in with or X");
32282             return;
32283         }
32284         this.currentSize = cs;
32285         this.layout();
32286     },
32287     
32288     layout : function()
32289     {
32290          Roo.log('layout');
32291         this._resetLayout();
32292         //this._manageStamps();
32293       
32294         // don't animate first layout
32295         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32296         this.layoutItems( isInstant );
32297       
32298         // flag for initalized
32299         this._isLayoutInited = true;
32300     },
32301     
32302     layoutItems : function( isInstant )
32303     {
32304         //var items = this._getItemsForLayout( this.items );
32305         // original code supports filtering layout items.. we just ignore it..
32306         
32307         this._layoutItems( this.bricks , isInstant );
32308       
32309         this._postLayout();
32310     },
32311     _layoutItems : function ( items , isInstant)
32312     {
32313        //this.fireEvent( 'layout', this, items );
32314     
32315
32316         if ( !items || !items.elements.length ) {
32317           // no items, emit event with empty array
32318             return;
32319         }
32320
32321         var queue = [];
32322         items.each(function(item) {
32323             Roo.log("layout item");
32324             Roo.log(item);
32325             // get x/y object from method
32326             var position = this._getItemLayoutPosition( item );
32327             // enqueue
32328             position.item = item;
32329             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32330             queue.push( position );
32331         }, this);
32332       
32333         this._processLayoutQueue( queue );
32334     },
32335     /** Sets position of item in DOM
32336     * @param {Element} item
32337     * @param {Number} x - horizontal position
32338     * @param {Number} y - vertical position
32339     * @param {Boolean} isInstant - disables transitions
32340     */
32341     _processLayoutQueue : function( queue )
32342     {
32343         for ( var i=0, len = queue.length; i < len; i++ ) {
32344             var obj = queue[i];
32345             obj.item.position('absolute');
32346             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32347         }
32348     },
32349       
32350     
32351     /**
32352     * Any logic you want to do after each layout,
32353     * i.e. size the container
32354     */
32355     _postLayout : function()
32356     {
32357         this.resizeContainer();
32358     },
32359     
32360     resizeContainer : function()
32361     {
32362         if ( !this.isResizingContainer ) {
32363             return;
32364         }
32365         var size = this._getContainerSize();
32366         if ( size ) {
32367             this.el.setSize(size.width,size.height);
32368             this.boxesEl.setSize(size.width,size.height);
32369         }
32370     },
32371     
32372     
32373     
32374     _resetLayout : function()
32375     {
32376         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32377         this.colWidth = this.el.getWidth();
32378         //this.gutter = this.el.getWidth(); 
32379         
32380         this.measureColumns();
32381
32382         // reset column Y
32383         var i = this.cols;
32384         this.colYs = [];
32385         while (i--) {
32386             this.colYs.push( 0 );
32387         }
32388     
32389         this.maxY = 0;
32390     },
32391
32392     measureColumns : function()
32393     {
32394         this.getContainerWidth();
32395       // if columnWidth is 0, default to outerWidth of first item
32396         if ( !this.columnWidth ) {
32397             var firstItem = this.bricks.first();
32398             Roo.log(firstItem);
32399             this.columnWidth  = this.containerWidth;
32400             if (firstItem && firstItem.attr('originalwidth') ) {
32401                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32402             }
32403             // columnWidth fall back to item of first element
32404             Roo.log("set column width?");
32405                         this.initialColumnWidth = this.columnWidth  ;
32406
32407             // if first elem has no width, default to size of container
32408             
32409         }
32410         
32411         
32412         if (this.initialColumnWidth) {
32413             this.columnWidth = this.initialColumnWidth;
32414         }
32415         
32416         
32417             
32418         // column width is fixed at the top - however if container width get's smaller we should
32419         // reduce it...
32420         
32421         // this bit calcs how man columns..
32422             
32423         var columnWidth = this.columnWidth += this.gutter;
32424       
32425         // calculate columns
32426         var containerWidth = this.containerWidth + this.gutter;
32427         
32428         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32429         // fix rounding errors, typically with gutters
32430         var excess = columnWidth - containerWidth % columnWidth;
32431         
32432         
32433         // if overshoot is less than a pixel, round up, otherwise floor it
32434         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32435         cols = Math[ mathMethod ]( cols );
32436         this.cols = Math.max( cols, 1 );
32437         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32438         
32439          // padding positioning..
32440         var totalColWidth = this.cols * this.columnWidth;
32441         var padavail = this.containerWidth - totalColWidth;
32442         // so for 2 columns - we need 3 'pads'
32443         
32444         var padNeeded = (1+this.cols) * this.padWidth;
32445         
32446         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32447         
32448         this.columnWidth += padExtra
32449         //this.padWidth = Math.floor(padavail /  ( this.cols));
32450         
32451         // adjust colum width so that padding is fixed??
32452         
32453         // we have 3 columns ... total = width * 3
32454         // we have X left over... that should be used by 
32455         
32456         //if (this.expandC) {
32457             
32458         //}
32459         
32460         
32461         
32462     },
32463     
32464     getContainerWidth : function()
32465     {
32466        /* // container is parent if fit width
32467         var container = this.isFitWidth ? this.element.parentNode : this.element;
32468         // check that this.size and size are there
32469         // IE8 triggers resize on body size change, so they might not be
32470         
32471         var size = getSize( container );  //FIXME
32472         this.containerWidth = size && size.innerWidth; //FIXME
32473         */
32474          
32475         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32476         
32477     },
32478     
32479     _getItemLayoutPosition : function( item )  // what is item?
32480     {
32481         // we resize the item to our columnWidth..
32482       
32483         item.setWidth(this.columnWidth);
32484         item.autoBoxAdjust  = false;
32485         
32486         var sz = item.getSize();
32487  
32488         // how many columns does this brick span
32489         var remainder = this.containerWidth % this.columnWidth;
32490         
32491         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32492         // round if off by 1 pixel, otherwise use ceil
32493         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32494         colSpan = Math.min( colSpan, this.cols );
32495         
32496         // normally this should be '1' as we dont' currently allow multi width columns..
32497         
32498         var colGroup = this._getColGroup( colSpan );
32499         // get the minimum Y value from the columns
32500         var minimumY = Math.min.apply( Math, colGroup );
32501         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32502         
32503         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32504          
32505         // position the brick
32506         var position = {
32507             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32508             y: this.currentSize.y + minimumY + this.padHeight
32509         };
32510         
32511         Roo.log(position);
32512         // apply setHeight to necessary columns
32513         var setHeight = minimumY + sz.height + this.padHeight;
32514         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32515         
32516         var setSpan = this.cols + 1 - colGroup.length;
32517         for ( var i = 0; i < setSpan; i++ ) {
32518           this.colYs[ shortColIndex + i ] = setHeight ;
32519         }
32520       
32521         return position;
32522     },
32523     
32524     /**
32525      * @param {Number} colSpan - number of columns the element spans
32526      * @returns {Array} colGroup
32527      */
32528     _getColGroup : function( colSpan )
32529     {
32530         if ( colSpan < 2 ) {
32531           // if brick spans only one column, use all the column Ys
32532           return this.colYs;
32533         }
32534       
32535         var colGroup = [];
32536         // how many different places could this brick fit horizontally
32537         var groupCount = this.cols + 1 - colSpan;
32538         // for each group potential horizontal position
32539         for ( var i = 0; i < groupCount; i++ ) {
32540           // make an array of colY values for that one group
32541           var groupColYs = this.colYs.slice( i, i + colSpan );
32542           // and get the max value of the array
32543           colGroup[i] = Math.max.apply( Math, groupColYs );
32544         }
32545         return colGroup;
32546     },
32547     /*
32548     _manageStamp : function( stamp )
32549     {
32550         var stampSize =  stamp.getSize();
32551         var offset = stamp.getBox();
32552         // get the columns that this stamp affects
32553         var firstX = this.isOriginLeft ? offset.x : offset.right;
32554         var lastX = firstX + stampSize.width;
32555         var firstCol = Math.floor( firstX / this.columnWidth );
32556         firstCol = Math.max( 0, firstCol );
32557         
32558         var lastCol = Math.floor( lastX / this.columnWidth );
32559         // lastCol should not go over if multiple of columnWidth #425
32560         lastCol -= lastX % this.columnWidth ? 0 : 1;
32561         lastCol = Math.min( this.cols - 1, lastCol );
32562         
32563         // set colYs to bottom of the stamp
32564         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32565             stampSize.height;
32566             
32567         for ( var i = firstCol; i <= lastCol; i++ ) {
32568           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32569         }
32570     },
32571     */
32572     
32573     _getContainerSize : function()
32574     {
32575         this.maxY = Math.max.apply( Math, this.colYs );
32576         var size = {
32577             height: this.maxY
32578         };
32579       
32580         if ( this.isFitWidth ) {
32581             size.width = this._getContainerFitWidth();
32582         }
32583       
32584         return size;
32585     },
32586     
32587     _getContainerFitWidth : function()
32588     {
32589         var unusedCols = 0;
32590         // count unused columns
32591         var i = this.cols;
32592         while ( --i ) {
32593           if ( this.colYs[i] !== 0 ) {
32594             break;
32595           }
32596           unusedCols++;
32597         }
32598         // fit container to columns that have been used
32599         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32600     },
32601     
32602     needsResizeLayout : function()
32603     {
32604         var previousWidth = this.containerWidth;
32605         this.getContainerWidth();
32606         return previousWidth !== this.containerWidth;
32607     }
32608  
32609 });
32610
32611  
32612
32613  /*
32614  * - LGPL
32615  *
32616  * element
32617  * 
32618  */
32619
32620 /**
32621  * @class Roo.bootstrap.MasonryBrick
32622  * @extends Roo.bootstrap.Component
32623  * Bootstrap MasonryBrick class
32624  * 
32625  * @constructor
32626  * Create a new MasonryBrick
32627  * @param {Object} config The config object
32628  */
32629
32630 Roo.bootstrap.MasonryBrick = function(config){
32631     
32632     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32633     
32634     Roo.bootstrap.MasonryBrick.register(this);
32635     
32636     this.addEvents({
32637         // raw events
32638         /**
32639          * @event click
32640          * When a MasonryBrick is clcik
32641          * @param {Roo.bootstrap.MasonryBrick} this
32642          * @param {Roo.EventObject} e
32643          */
32644         "click" : true
32645     });
32646 };
32647
32648 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32649     
32650     /**
32651      * @cfg {String} title
32652      */   
32653     title : '',
32654     /**
32655      * @cfg {String} html
32656      */   
32657     html : '',
32658     /**
32659      * @cfg {String} bgimage
32660      */   
32661     bgimage : '',
32662     /**
32663      * @cfg {String} videourl
32664      */   
32665     videourl : '',
32666     /**
32667      * @cfg {String} cls
32668      */   
32669     cls : '',
32670     /**
32671      * @cfg {String} href
32672      */   
32673     href : '',
32674     /**
32675      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32676      */   
32677     size : 'xs',
32678     
32679     /**
32680      * @cfg {String} placetitle (center|bottom)
32681      */   
32682     placetitle : '',
32683     
32684     /**
32685      * @cfg {Boolean} isFitContainer defalut true
32686      */   
32687     isFitContainer : true, 
32688     
32689     /**
32690      * @cfg {Boolean} preventDefault defalut false
32691      */   
32692     preventDefault : false, 
32693     
32694     /**
32695      * @cfg {Boolean} inverse defalut false
32696      */   
32697     maskInverse : false, 
32698     
32699     getAutoCreate : function()
32700     {
32701         if(!this.isFitContainer){
32702             return this.getSplitAutoCreate();
32703         }
32704         
32705         var cls = 'masonry-brick masonry-brick-full';
32706         
32707         if(this.href.length){
32708             cls += ' masonry-brick-link';
32709         }
32710         
32711         if(this.bgimage.length){
32712             cls += ' masonry-brick-image';
32713         }
32714         
32715         if(this.maskInverse){
32716             cls += ' mask-inverse';
32717         }
32718         
32719         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32720             cls += ' enable-mask';
32721         }
32722         
32723         if(this.size){
32724             cls += ' masonry-' + this.size + '-brick';
32725         }
32726         
32727         if(this.placetitle.length){
32728             
32729             switch (this.placetitle) {
32730                 case 'center' :
32731                     cls += ' masonry-center-title';
32732                     break;
32733                 case 'bottom' :
32734                     cls += ' masonry-bottom-title';
32735                     break;
32736                 default:
32737                     break;
32738             }
32739             
32740         } else {
32741             if(!this.html.length && !this.bgimage.length){
32742                 cls += ' masonry-center-title';
32743             }
32744
32745             if(!this.html.length && this.bgimage.length){
32746                 cls += ' masonry-bottom-title';
32747             }
32748         }
32749         
32750         if(this.cls){
32751             cls += ' ' + this.cls;
32752         }
32753         
32754         var cfg = {
32755             tag: (this.href.length) ? 'a' : 'div',
32756             cls: cls,
32757             cn: [
32758                 {
32759                     tag: 'div',
32760                     cls: 'masonry-brick-mask'
32761                 },
32762                 {
32763                     tag: 'div',
32764                     cls: 'masonry-brick-paragraph',
32765                     cn: []
32766                 }
32767             ]
32768         };
32769         
32770         if(this.href.length){
32771             cfg.href = this.href;
32772         }
32773         
32774         var cn = cfg.cn[1].cn;
32775         
32776         if(this.title.length){
32777             cn.push({
32778                 tag: 'h4',
32779                 cls: 'masonry-brick-title',
32780                 html: this.title
32781             });
32782         }
32783         
32784         if(this.html.length){
32785             cn.push({
32786                 tag: 'p',
32787                 cls: 'masonry-brick-text',
32788                 html: this.html
32789             });
32790         }
32791         
32792         if (!this.title.length && !this.html.length) {
32793             cfg.cn[1].cls += ' hide';
32794         }
32795         
32796         if(this.bgimage.length){
32797             cfg.cn.push({
32798                 tag: 'img',
32799                 cls: 'masonry-brick-image-view',
32800                 src: this.bgimage
32801             });
32802         }
32803         
32804         if(this.videourl.length){
32805             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32806             // youtube support only?
32807             cfg.cn.push({
32808                 tag: 'iframe',
32809                 cls: 'masonry-brick-image-view',
32810                 src: vurl,
32811                 frameborder : 0,
32812                 allowfullscreen : true
32813             });
32814         }
32815         
32816         return cfg;
32817         
32818     },
32819     
32820     getSplitAutoCreate : function()
32821     {
32822         var cls = 'masonry-brick masonry-brick-split';
32823         
32824         if(this.href.length){
32825             cls += ' masonry-brick-link';
32826         }
32827         
32828         if(this.bgimage.length){
32829             cls += ' masonry-brick-image';
32830         }
32831         
32832         if(this.size){
32833             cls += ' masonry-' + this.size + '-brick';
32834         }
32835         
32836         switch (this.placetitle) {
32837             case 'center' :
32838                 cls += ' masonry-center-title';
32839                 break;
32840             case 'bottom' :
32841                 cls += ' masonry-bottom-title';
32842                 break;
32843             default:
32844                 if(!this.bgimage.length){
32845                     cls += ' masonry-center-title';
32846                 }
32847
32848                 if(this.bgimage.length){
32849                     cls += ' masonry-bottom-title';
32850                 }
32851                 break;
32852         }
32853         
32854         if(this.cls){
32855             cls += ' ' + this.cls;
32856         }
32857         
32858         var cfg = {
32859             tag: (this.href.length) ? 'a' : 'div',
32860             cls: cls,
32861             cn: [
32862                 {
32863                     tag: 'div',
32864                     cls: 'masonry-brick-split-head',
32865                     cn: [
32866                         {
32867                             tag: 'div',
32868                             cls: 'masonry-brick-paragraph',
32869                             cn: []
32870                         }
32871                     ]
32872                 },
32873                 {
32874                     tag: 'div',
32875                     cls: 'masonry-brick-split-body',
32876                     cn: []
32877                 }
32878             ]
32879         };
32880         
32881         if(this.href.length){
32882             cfg.href = this.href;
32883         }
32884         
32885         if(this.title.length){
32886             cfg.cn[0].cn[0].cn.push({
32887                 tag: 'h4',
32888                 cls: 'masonry-brick-title',
32889                 html: this.title
32890             });
32891         }
32892         
32893         if(this.html.length){
32894             cfg.cn[1].cn.push({
32895                 tag: 'p',
32896                 cls: 'masonry-brick-text',
32897                 html: this.html
32898             });
32899         }
32900
32901         if(this.bgimage.length){
32902             cfg.cn[0].cn.push({
32903                 tag: 'img',
32904                 cls: 'masonry-brick-image-view',
32905                 src: this.bgimage
32906             });
32907         }
32908         
32909         if(this.videourl.length){
32910             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32911             // youtube support only?
32912             cfg.cn[0].cn.cn.push({
32913                 tag: 'iframe',
32914                 cls: 'masonry-brick-image-view',
32915                 src: vurl,
32916                 frameborder : 0,
32917                 allowfullscreen : true
32918             });
32919         }
32920         
32921         return cfg;
32922     },
32923     
32924     initEvents: function() 
32925     {
32926         switch (this.size) {
32927             case 'xs' :
32928                 this.x = 1;
32929                 this.y = 1;
32930                 break;
32931             case 'sm' :
32932                 this.x = 2;
32933                 this.y = 2;
32934                 break;
32935             case 'md' :
32936             case 'md-left' :
32937             case 'md-right' :
32938                 this.x = 3;
32939                 this.y = 3;
32940                 break;
32941             case 'tall' :
32942                 this.x = 2;
32943                 this.y = 3;
32944                 break;
32945             case 'wide' :
32946                 this.x = 3;
32947                 this.y = 2;
32948                 break;
32949             case 'wide-thin' :
32950                 this.x = 3;
32951                 this.y = 1;
32952                 break;
32953                         
32954             default :
32955                 break;
32956         }
32957         
32958         if(Roo.isTouch){
32959             this.el.on('touchstart', this.onTouchStart, this);
32960             this.el.on('touchmove', this.onTouchMove, this);
32961             this.el.on('touchend', this.onTouchEnd, this);
32962             this.el.on('contextmenu', this.onContextMenu, this);
32963         } else {
32964             this.el.on('mouseenter'  ,this.enter, this);
32965             this.el.on('mouseleave', this.leave, this);
32966             this.el.on('click', this.onClick, this);
32967         }
32968         
32969         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32970             this.parent().bricks.push(this);   
32971         }
32972         
32973     },
32974     
32975     onClick: function(e, el)
32976     {
32977         var time = this.endTimer - this.startTimer;
32978         // Roo.log(e.preventDefault());
32979         if(Roo.isTouch){
32980             if(time > 1000){
32981                 e.preventDefault();
32982                 return;
32983             }
32984         }
32985         
32986         if(!this.preventDefault){
32987             return;
32988         }
32989         
32990         e.preventDefault();
32991         
32992         if (this.activeClass != '') {
32993             this.selectBrick();
32994         }
32995         
32996         this.fireEvent('click', this, e);
32997     },
32998     
32999     enter: function(e, el)
33000     {
33001         e.preventDefault();
33002         
33003         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33004             return;
33005         }
33006         
33007         if(this.bgimage.length && this.html.length){
33008             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33009         }
33010     },
33011     
33012     leave: function(e, el)
33013     {
33014         e.preventDefault();
33015         
33016         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33017             return;
33018         }
33019         
33020         if(this.bgimage.length && this.html.length){
33021             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33022         }
33023     },
33024     
33025     onTouchStart: function(e, el)
33026     {
33027 //        e.preventDefault();
33028         
33029         this.touchmoved = false;
33030         
33031         if(!this.isFitContainer){
33032             return;
33033         }
33034         
33035         if(!this.bgimage.length || !this.html.length){
33036             return;
33037         }
33038         
33039         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33040         
33041         this.timer = new Date().getTime();
33042         
33043     },
33044     
33045     onTouchMove: function(e, el)
33046     {
33047         this.touchmoved = true;
33048     },
33049     
33050     onContextMenu : function(e,el)
33051     {
33052         e.preventDefault();
33053         e.stopPropagation();
33054         return false;
33055     },
33056     
33057     onTouchEnd: function(e, el)
33058     {
33059 //        e.preventDefault();
33060         
33061         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33062         
33063             this.leave(e,el);
33064             
33065             return;
33066         }
33067         
33068         if(!this.bgimage.length || !this.html.length){
33069             
33070             if(this.href.length){
33071                 window.location.href = this.href;
33072             }
33073             
33074             return;
33075         }
33076         
33077         if(!this.isFitContainer){
33078             return;
33079         }
33080         
33081         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33082         
33083         window.location.href = this.href;
33084     },
33085     
33086     //selection on single brick only
33087     selectBrick : function() {
33088         
33089         if (!this.parentId) {
33090             return;
33091         }
33092         
33093         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33094         var index = m.selectedBrick.indexOf(this.id);
33095         
33096         if ( index > -1) {
33097             m.selectedBrick.splice(index,1);
33098             this.el.removeClass(this.activeClass);
33099             return;
33100         }
33101         
33102         for(var i = 0; i < m.selectedBrick.length; i++) {
33103             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33104             b.el.removeClass(b.activeClass);
33105         }
33106         
33107         m.selectedBrick = [];
33108         
33109         m.selectedBrick.push(this.id);
33110         this.el.addClass(this.activeClass);
33111         return;
33112     },
33113     
33114     isSelected : function(){
33115         return this.el.hasClass(this.activeClass);
33116         
33117     }
33118 });
33119
33120 Roo.apply(Roo.bootstrap.MasonryBrick, {
33121     
33122     //groups: {},
33123     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33124      /**
33125     * register a Masonry Brick
33126     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33127     */
33128     
33129     register : function(brick)
33130     {
33131         //this.groups[brick.id] = brick;
33132         this.groups.add(brick.id, brick);
33133     },
33134     /**
33135     * fetch a  masonry brick based on the masonry brick ID
33136     * @param {string} the masonry brick to add
33137     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33138     */
33139     
33140     get: function(brick_id) 
33141     {
33142         // if (typeof(this.groups[brick_id]) == 'undefined') {
33143         //     return false;
33144         // }
33145         // return this.groups[brick_id] ;
33146         
33147         if(this.groups.key(brick_id)) {
33148             return this.groups.key(brick_id);
33149         }
33150         
33151         return false;
33152     }
33153     
33154     
33155     
33156 });
33157
33158  /*
33159  * - LGPL
33160  *
33161  * element
33162  * 
33163  */
33164
33165 /**
33166  * @class Roo.bootstrap.Brick
33167  * @extends Roo.bootstrap.Component
33168  * Bootstrap Brick class
33169  * 
33170  * @constructor
33171  * Create a new Brick
33172  * @param {Object} config The config object
33173  */
33174
33175 Roo.bootstrap.Brick = function(config){
33176     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33177     
33178     this.addEvents({
33179         // raw events
33180         /**
33181          * @event click
33182          * When a Brick is click
33183          * @param {Roo.bootstrap.Brick} this
33184          * @param {Roo.EventObject} e
33185          */
33186         "click" : true
33187     });
33188 };
33189
33190 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33191     
33192     /**
33193      * @cfg {String} title
33194      */   
33195     title : '',
33196     /**
33197      * @cfg {String} html
33198      */   
33199     html : '',
33200     /**
33201      * @cfg {String} bgimage
33202      */   
33203     bgimage : '',
33204     /**
33205      * @cfg {String} cls
33206      */   
33207     cls : '',
33208     /**
33209      * @cfg {String} href
33210      */   
33211     href : '',
33212     /**
33213      * @cfg {String} video
33214      */   
33215     video : '',
33216     /**
33217      * @cfg {Boolean} square
33218      */   
33219     square : true,
33220     
33221     getAutoCreate : function()
33222     {
33223         var cls = 'roo-brick';
33224         
33225         if(this.href.length){
33226             cls += ' roo-brick-link';
33227         }
33228         
33229         if(this.bgimage.length){
33230             cls += ' roo-brick-image';
33231         }
33232         
33233         if(!this.html.length && !this.bgimage.length){
33234             cls += ' roo-brick-center-title';
33235         }
33236         
33237         if(!this.html.length && this.bgimage.length){
33238             cls += ' roo-brick-bottom-title';
33239         }
33240         
33241         if(this.cls){
33242             cls += ' ' + this.cls;
33243         }
33244         
33245         var cfg = {
33246             tag: (this.href.length) ? 'a' : 'div',
33247             cls: cls,
33248             cn: [
33249                 {
33250                     tag: 'div',
33251                     cls: 'roo-brick-paragraph',
33252                     cn: []
33253                 }
33254             ]
33255         };
33256         
33257         if(this.href.length){
33258             cfg.href = this.href;
33259         }
33260         
33261         var cn = cfg.cn[0].cn;
33262         
33263         if(this.title.length){
33264             cn.push({
33265                 tag: 'h4',
33266                 cls: 'roo-brick-title',
33267                 html: this.title
33268             });
33269         }
33270         
33271         if(this.html.length){
33272             cn.push({
33273                 tag: 'p',
33274                 cls: 'roo-brick-text',
33275                 html: this.html
33276             });
33277         } else {
33278             cn.cls += ' hide';
33279         }
33280         
33281         if(this.bgimage.length){
33282             cfg.cn.push({
33283                 tag: 'img',
33284                 cls: 'roo-brick-image-view',
33285                 src: this.bgimage
33286             });
33287         }
33288         
33289         return cfg;
33290     },
33291     
33292     initEvents: function() 
33293     {
33294         if(this.title.length || this.html.length){
33295             this.el.on('mouseenter'  ,this.enter, this);
33296             this.el.on('mouseleave', this.leave, this);
33297         }
33298         
33299         Roo.EventManager.onWindowResize(this.resize, this); 
33300         
33301         if(this.bgimage.length){
33302             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33303             this.imageEl.on('load', this.onImageLoad, this);
33304             return;
33305         }
33306         
33307         this.resize();
33308     },
33309     
33310     onImageLoad : function()
33311     {
33312         this.resize();
33313     },
33314     
33315     resize : function()
33316     {
33317         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33318         
33319         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33320         
33321         if(this.bgimage.length){
33322             var image = this.el.select('.roo-brick-image-view', true).first();
33323             
33324             image.setWidth(paragraph.getWidth());
33325             
33326             if(this.square){
33327                 image.setHeight(paragraph.getWidth());
33328             }
33329             
33330             this.el.setHeight(image.getHeight());
33331             paragraph.setHeight(image.getHeight());
33332             
33333         }
33334         
33335     },
33336     
33337     enter: function(e, el)
33338     {
33339         e.preventDefault();
33340         
33341         if(this.bgimage.length){
33342             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33343             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33344         }
33345     },
33346     
33347     leave: function(e, el)
33348     {
33349         e.preventDefault();
33350         
33351         if(this.bgimage.length){
33352             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33353             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33354         }
33355     }
33356     
33357 });
33358
33359  
33360
33361  /*
33362  * - LGPL
33363  *
33364  * Number field 
33365  */
33366
33367 /**
33368  * @class Roo.bootstrap.NumberField
33369  * @extends Roo.bootstrap.Input
33370  * Bootstrap NumberField class
33371  * 
33372  * 
33373  * 
33374  * 
33375  * @constructor
33376  * Create a new NumberField
33377  * @param {Object} config The config object
33378  */
33379
33380 Roo.bootstrap.NumberField = function(config){
33381     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33382 };
33383
33384 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33385     
33386     /**
33387      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33388      */
33389     allowDecimals : true,
33390     /**
33391      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33392      */
33393     decimalSeparator : ".",
33394     /**
33395      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33396      */
33397     decimalPrecision : 2,
33398     /**
33399      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33400      */
33401     allowNegative : true,
33402     
33403     /**
33404      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33405      */
33406     allowZero: true,
33407     /**
33408      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33409      */
33410     minValue : Number.NEGATIVE_INFINITY,
33411     /**
33412      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33413      */
33414     maxValue : Number.MAX_VALUE,
33415     /**
33416      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33417      */
33418     minText : "The minimum value for this field is {0}",
33419     /**
33420      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33421      */
33422     maxText : "The maximum value for this field is {0}",
33423     /**
33424      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33425      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33426      */
33427     nanText : "{0} is not a valid number",
33428     /**
33429      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33430      */
33431     thousandsDelimiter : false,
33432     /**
33433      * @cfg {String} valueAlign alignment of value
33434      */
33435     valueAlign : "left",
33436
33437     getAutoCreate : function()
33438     {
33439         var hiddenInput = {
33440             tag: 'input',
33441             type: 'hidden',
33442             id: Roo.id(),
33443             cls: 'hidden-number-input'
33444         };
33445         
33446         if (this.name) {
33447             hiddenInput.name = this.name;
33448         }
33449         
33450         this.name = '';
33451         
33452         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33453         
33454         this.name = hiddenInput.name;
33455         
33456         if(cfg.cn.length > 0) {
33457             cfg.cn.push(hiddenInput);
33458         }
33459         
33460         return cfg;
33461     },
33462
33463     // private
33464     initEvents : function()
33465     {   
33466         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33467         
33468         var allowed = "0123456789";
33469         
33470         if(this.allowDecimals){
33471             allowed += this.decimalSeparator;
33472         }
33473         
33474         if(this.allowNegative){
33475             allowed += "-";
33476         }
33477         
33478         if(this.thousandsDelimiter) {
33479             allowed += ",";
33480         }
33481         
33482         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33483         
33484         var keyPress = function(e){
33485             
33486             var k = e.getKey();
33487             
33488             var c = e.getCharCode();
33489             
33490             if(
33491                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33492                     allowed.indexOf(String.fromCharCode(c)) === -1
33493             ){
33494                 e.stopEvent();
33495                 return;
33496             }
33497             
33498             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33499                 return;
33500             }
33501             
33502             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33503                 e.stopEvent();
33504             }
33505         };
33506         
33507         this.el.on("keypress", keyPress, this);
33508     },
33509     
33510     validateValue : function(value)
33511     {
33512         
33513         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33514             return false;
33515         }
33516         
33517         var num = this.parseValue(value);
33518         
33519         if(isNaN(num)){
33520             this.markInvalid(String.format(this.nanText, value));
33521             return false;
33522         }
33523         
33524         if(num < this.minValue){
33525             this.markInvalid(String.format(this.minText, this.minValue));
33526             return false;
33527         }
33528         
33529         if(num > this.maxValue){
33530             this.markInvalid(String.format(this.maxText, this.maxValue));
33531             return false;
33532         }
33533         
33534         return true;
33535     },
33536
33537     getValue : function()
33538     {
33539         var v = this.hiddenEl().getValue();
33540         
33541         return this.fixPrecision(this.parseValue(v));
33542     },
33543
33544     parseValue : function(value)
33545     {
33546         if(this.thousandsDelimiter) {
33547             value += "";
33548             r = new RegExp(",", "g");
33549             value = value.replace(r, "");
33550         }
33551         
33552         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33553         return isNaN(value) ? '' : value;
33554     },
33555
33556     fixPrecision : function(value)
33557     {
33558         if(this.thousandsDelimiter) {
33559             value += "";
33560             r = new RegExp(",", "g");
33561             value = value.replace(r, "");
33562         }
33563         
33564         var nan = isNaN(value);
33565         
33566         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33567             return nan ? '' : value;
33568         }
33569         return parseFloat(value).toFixed(this.decimalPrecision);
33570     },
33571
33572     setValue : function(v)
33573     {
33574         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33575         
33576         this.value = v;
33577         
33578         if(this.rendered){
33579             
33580             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33581             
33582             this.inputEl().dom.value = (v == '') ? '' :
33583                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33584             
33585             if(!this.allowZero && v === '0') {
33586                 this.hiddenEl().dom.value = '';
33587                 this.inputEl().dom.value = '';
33588             }
33589             
33590             this.validate();
33591         }
33592     },
33593
33594     decimalPrecisionFcn : function(v)
33595     {
33596         return Math.floor(v);
33597     },
33598
33599     beforeBlur : function()
33600     {
33601         var v = this.parseValue(this.getRawValue());
33602         
33603         if(v || v === 0 || v === ''){
33604             this.setValue(v);
33605         }
33606     },
33607     
33608     hiddenEl : function()
33609     {
33610         return this.el.select('input.hidden-number-input',true).first();
33611     }
33612     
33613 });
33614
33615  
33616
33617 /*
33618 * Licence: LGPL
33619 */
33620
33621 /**
33622  * @class Roo.bootstrap.DocumentSlider
33623  * @extends Roo.bootstrap.Component
33624  * Bootstrap DocumentSlider class
33625  * 
33626  * @constructor
33627  * Create a new DocumentViewer
33628  * @param {Object} config The config object
33629  */
33630
33631 Roo.bootstrap.DocumentSlider = function(config){
33632     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33633     
33634     this.files = [];
33635     
33636     this.addEvents({
33637         /**
33638          * @event initial
33639          * Fire after initEvent
33640          * @param {Roo.bootstrap.DocumentSlider} this
33641          */
33642         "initial" : true,
33643         /**
33644          * @event update
33645          * Fire after update
33646          * @param {Roo.bootstrap.DocumentSlider} this
33647          */
33648         "update" : true,
33649         /**
33650          * @event click
33651          * Fire after click
33652          * @param {Roo.bootstrap.DocumentSlider} this
33653          */
33654         "click" : true
33655     });
33656 };
33657
33658 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33659     
33660     files : false,
33661     
33662     indicator : 0,
33663     
33664     getAutoCreate : function()
33665     {
33666         var cfg = {
33667             tag : 'div',
33668             cls : 'roo-document-slider',
33669             cn : [
33670                 {
33671                     tag : 'div',
33672                     cls : 'roo-document-slider-header',
33673                     cn : [
33674                         {
33675                             tag : 'div',
33676                             cls : 'roo-document-slider-header-title'
33677                         }
33678                     ]
33679                 },
33680                 {
33681                     tag : 'div',
33682                     cls : 'roo-document-slider-body',
33683                     cn : [
33684                         {
33685                             tag : 'div',
33686                             cls : 'roo-document-slider-prev',
33687                             cn : [
33688                                 {
33689                                     tag : 'i',
33690                                     cls : 'fa fa-chevron-left'
33691                                 }
33692                             ]
33693                         },
33694                         {
33695                             tag : 'div',
33696                             cls : 'roo-document-slider-thumb',
33697                             cn : [
33698                                 {
33699                                     tag : 'img',
33700                                     cls : 'roo-document-slider-image'
33701                                 }
33702                             ]
33703                         },
33704                         {
33705                             tag : 'div',
33706                             cls : 'roo-document-slider-next',
33707                             cn : [
33708                                 {
33709                                     tag : 'i',
33710                                     cls : 'fa fa-chevron-right'
33711                                 }
33712                             ]
33713                         }
33714                     ]
33715                 }
33716             ]
33717         };
33718         
33719         return cfg;
33720     },
33721     
33722     initEvents : function()
33723     {
33724         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33725         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33726         
33727         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33728         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33729         
33730         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33731         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33732         
33733         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33734         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33735         
33736         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33737         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33738         
33739         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33740         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33741         
33742         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33743         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33744         
33745         this.thumbEl.on('click', this.onClick, this);
33746         
33747         this.prevIndicator.on('click', this.prev, this);
33748         
33749         this.nextIndicator.on('click', this.next, this);
33750         
33751     },
33752     
33753     initial : function()
33754     {
33755         if(this.files.length){
33756             this.indicator = 1;
33757             this.update()
33758         }
33759         
33760         this.fireEvent('initial', this);
33761     },
33762     
33763     update : function()
33764     {
33765         this.imageEl.attr('src', this.files[this.indicator - 1]);
33766         
33767         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33768         
33769         this.prevIndicator.show();
33770         
33771         if(this.indicator == 1){
33772             this.prevIndicator.hide();
33773         }
33774         
33775         this.nextIndicator.show();
33776         
33777         if(this.indicator == this.files.length){
33778             this.nextIndicator.hide();
33779         }
33780         
33781         this.thumbEl.scrollTo('top');
33782         
33783         this.fireEvent('update', this);
33784     },
33785     
33786     onClick : function(e)
33787     {
33788         e.preventDefault();
33789         
33790         this.fireEvent('click', this);
33791     },
33792     
33793     prev : function(e)
33794     {
33795         e.preventDefault();
33796         
33797         this.indicator = Math.max(1, this.indicator - 1);
33798         
33799         this.update();
33800     },
33801     
33802     next : function(e)
33803     {
33804         e.preventDefault();
33805         
33806         this.indicator = Math.min(this.files.length, this.indicator + 1);
33807         
33808         this.update();
33809     }
33810 });
33811 /*
33812  * - LGPL
33813  *
33814  * RadioSet
33815  *
33816  *
33817  */
33818
33819 /**
33820  * @class Roo.bootstrap.RadioSet
33821  * @extends Roo.bootstrap.Input
33822  * Bootstrap RadioSet class
33823  * @cfg {String} indicatorpos (left|right) default left
33824  * @cfg {Boolean} inline (true|false) inline the element (default true)
33825  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33826  * @constructor
33827  * Create a new RadioSet
33828  * @param {Object} config The config object
33829  */
33830
33831 Roo.bootstrap.RadioSet = function(config){
33832     
33833     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33834     
33835     this.radioes = [];
33836     
33837     Roo.bootstrap.RadioSet.register(this);
33838     
33839     this.addEvents({
33840         /**
33841         * @event check
33842         * Fires when the element is checked or unchecked.
33843         * @param {Roo.bootstrap.RadioSet} this This radio
33844         * @param {Roo.bootstrap.Radio} item The checked item
33845         */
33846        check : true,
33847        /**
33848         * @event click
33849         * Fires when the element is click.
33850         * @param {Roo.bootstrap.RadioSet} this This radio set
33851         * @param {Roo.bootstrap.Radio} item The checked item
33852         * @param {Roo.EventObject} e The event object
33853         */
33854        click : true
33855     });
33856     
33857 };
33858
33859 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33860
33861     radioes : false,
33862     
33863     inline : true,
33864     
33865     weight : '',
33866     
33867     indicatorpos : 'left',
33868     
33869     getAutoCreate : function()
33870     {
33871         var label = {
33872             tag : 'label',
33873             cls : 'roo-radio-set-label',
33874             cn : [
33875                 {
33876                     tag : 'span',
33877                     html : this.fieldLabel
33878                 }
33879             ]
33880         };
33881         
33882         if(this.indicatorpos == 'left'){
33883             label.cn.unshift({
33884                 tag : 'i',
33885                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33886                 tooltip : 'This field is required'
33887             });
33888         } else {
33889             label.cn.push({
33890                 tag : 'i',
33891                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33892                 tooltip : 'This field is required'
33893             });
33894         }
33895         
33896         var items = {
33897             tag : 'div',
33898             cls : 'roo-radio-set-items'
33899         };
33900         
33901         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33902         
33903         if (align === 'left' && this.fieldLabel.length) {
33904             
33905             items = {
33906                 cls : "roo-radio-set-right", 
33907                 cn: [
33908                     items
33909                 ]
33910             };
33911             
33912             if(this.labelWidth > 12){
33913                 label.style = "width: " + this.labelWidth + 'px';
33914             }
33915             
33916             if(this.labelWidth < 13 && this.labelmd == 0){
33917                 this.labelmd = this.labelWidth;
33918             }
33919             
33920             if(this.labellg > 0){
33921                 label.cls += ' col-lg-' + this.labellg;
33922                 items.cls += ' col-lg-' + (12 - this.labellg);
33923             }
33924             
33925             if(this.labelmd > 0){
33926                 label.cls += ' col-md-' + this.labelmd;
33927                 items.cls += ' col-md-' + (12 - this.labelmd);
33928             }
33929             
33930             if(this.labelsm > 0){
33931                 label.cls += ' col-sm-' + this.labelsm;
33932                 items.cls += ' col-sm-' + (12 - this.labelsm);
33933             }
33934             
33935             if(this.labelxs > 0){
33936                 label.cls += ' col-xs-' + this.labelxs;
33937                 items.cls += ' col-xs-' + (12 - this.labelxs);
33938             }
33939         }
33940         
33941         var cfg = {
33942             tag : 'div',
33943             cls : 'roo-radio-set',
33944             cn : [
33945                 {
33946                     tag : 'input',
33947                     cls : 'roo-radio-set-input',
33948                     type : 'hidden',
33949                     name : this.name,
33950                     value : this.value ? this.value :  ''
33951                 },
33952                 label,
33953                 items
33954             ]
33955         };
33956         
33957         if(this.weight.length){
33958             cfg.cls += ' roo-radio-' + this.weight;
33959         }
33960         
33961         if(this.inline) {
33962             cfg.cls += ' roo-radio-set-inline';
33963         }
33964         
33965         var settings=this;
33966         ['xs','sm','md','lg'].map(function(size){
33967             if (settings[size]) {
33968                 cfg.cls += ' col-' + size + '-' + settings[size];
33969             }
33970         });
33971         
33972         return cfg;
33973         
33974     },
33975
33976     initEvents : function()
33977     {
33978         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33979         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33980         
33981         if(!this.fieldLabel.length){
33982             this.labelEl.hide();
33983         }
33984         
33985         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33986         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33987         
33988         this.indicator = this.indicatorEl();
33989         
33990         if(this.indicator){
33991             this.indicator.addClass('invisible');
33992         }
33993         
33994         this.originalValue = this.getValue();
33995         
33996     },
33997     
33998     inputEl: function ()
33999     {
34000         return this.el.select('.roo-radio-set-input', true).first();
34001     },
34002     
34003     getChildContainer : function()
34004     {
34005         return this.itemsEl;
34006     },
34007     
34008     register : function(item)
34009     {
34010         this.radioes.push(item);
34011         
34012     },
34013     
34014     validate : function()
34015     {   
34016         if(this.getVisibilityEl().hasClass('hidden')){
34017             return true;
34018         }
34019         
34020         var valid = false;
34021         
34022         Roo.each(this.radioes, function(i){
34023             if(!i.checked){
34024                 return;
34025             }
34026             
34027             valid = true;
34028             return false;
34029         });
34030         
34031         if(this.allowBlank) {
34032             return true;
34033         }
34034         
34035         if(this.disabled || valid){
34036             this.markValid();
34037             return true;
34038         }
34039         
34040         this.markInvalid();
34041         return false;
34042         
34043     },
34044     
34045     markValid : function()
34046     {
34047         if(this.labelEl.isVisible(true)){
34048             this.indicatorEl().removeClass('visible');
34049             this.indicatorEl().addClass('invisible');
34050         }
34051         
34052         this.el.removeClass([this.invalidClass, this.validClass]);
34053         this.el.addClass(this.validClass);
34054         
34055         this.fireEvent('valid', this);
34056     },
34057     
34058     markInvalid : function(msg)
34059     {
34060         if(this.allowBlank || this.disabled){
34061             return;
34062         }
34063         
34064         if(this.labelEl.isVisible(true)){
34065             this.indicatorEl().removeClass('invisible');
34066             this.indicatorEl().addClass('visible');
34067         }
34068         
34069         this.el.removeClass([this.invalidClass, this.validClass]);
34070         this.el.addClass(this.invalidClass);
34071         
34072         this.fireEvent('invalid', this, msg);
34073         
34074     },
34075     
34076     setValue : function(v, suppressEvent)
34077     {   
34078         if(this.value === v){
34079             return;
34080         }
34081         
34082         this.value = v;
34083         
34084         if(this.rendered){
34085             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34086         }
34087         
34088         Roo.each(this.radioes, function(i){
34089             i.checked = false;
34090             i.el.removeClass('checked');
34091         });
34092         
34093         Roo.each(this.radioes, function(i){
34094             
34095             if(i.value === v || i.value.toString() === v.toString()){
34096                 i.checked = true;
34097                 i.el.addClass('checked');
34098                 
34099                 if(suppressEvent !== true){
34100                     this.fireEvent('check', this, i);
34101                 }
34102                 
34103                 return false;
34104             }
34105             
34106         }, this);
34107         
34108         this.validate();
34109     },
34110     
34111     clearInvalid : function(){
34112         
34113         if(!this.el || this.preventMark){
34114             return;
34115         }
34116         
34117         this.el.removeClass([this.invalidClass]);
34118         
34119         this.fireEvent('valid', this);
34120     }
34121     
34122 });
34123
34124 Roo.apply(Roo.bootstrap.RadioSet, {
34125     
34126     groups: {},
34127     
34128     register : function(set)
34129     {
34130         this.groups[set.name] = set;
34131     },
34132     
34133     get: function(name) 
34134     {
34135         if (typeof(this.groups[name]) == 'undefined') {
34136             return false;
34137         }
34138         
34139         return this.groups[name] ;
34140     }
34141     
34142 });
34143 /*
34144  * Based on:
34145  * Ext JS Library 1.1.1
34146  * Copyright(c) 2006-2007, Ext JS, LLC.
34147  *
34148  * Originally Released Under LGPL - original licence link has changed is not relivant.
34149  *
34150  * Fork - LGPL
34151  * <script type="text/javascript">
34152  */
34153
34154
34155 /**
34156  * @class Roo.bootstrap.SplitBar
34157  * @extends Roo.util.Observable
34158  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34159  * <br><br>
34160  * Usage:
34161  * <pre><code>
34162 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34163                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34164 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34165 split.minSize = 100;
34166 split.maxSize = 600;
34167 split.animate = true;
34168 split.on('moved', splitterMoved);
34169 </code></pre>
34170  * @constructor
34171  * Create a new SplitBar
34172  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34173  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34174  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34175  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34176                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34177                         position of the SplitBar).
34178  */
34179 Roo.bootstrap.SplitBar = function(cfg){
34180     
34181     /** @private */
34182     
34183     //{
34184     //  dragElement : elm
34185     //  resizingElement: el,
34186         // optional..
34187     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34188     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34189         // existingProxy ???
34190     //}
34191     
34192     this.el = Roo.get(cfg.dragElement, true);
34193     this.el.dom.unselectable = "on";
34194     /** @private */
34195     this.resizingEl = Roo.get(cfg.resizingElement, true);
34196
34197     /**
34198      * @private
34199      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34200      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34201      * @type Number
34202      */
34203     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34204     
34205     /**
34206      * The minimum size of the resizing element. (Defaults to 0)
34207      * @type Number
34208      */
34209     this.minSize = 0;
34210     
34211     /**
34212      * The maximum size of the resizing element. (Defaults to 2000)
34213      * @type Number
34214      */
34215     this.maxSize = 2000;
34216     
34217     /**
34218      * Whether to animate the transition to the new size
34219      * @type Boolean
34220      */
34221     this.animate = false;
34222     
34223     /**
34224      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34225      * @type Boolean
34226      */
34227     this.useShim = false;
34228     
34229     /** @private */
34230     this.shim = null;
34231     
34232     if(!cfg.existingProxy){
34233         /** @private */
34234         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34235     }else{
34236         this.proxy = Roo.get(cfg.existingProxy).dom;
34237     }
34238     /** @private */
34239     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34240     
34241     /** @private */
34242     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34243     
34244     /** @private */
34245     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34246     
34247     /** @private */
34248     this.dragSpecs = {};
34249     
34250     /**
34251      * @private The adapter to use to positon and resize elements
34252      */
34253     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34254     this.adapter.init(this);
34255     
34256     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34257         /** @private */
34258         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34259         this.el.addClass("roo-splitbar-h");
34260     }else{
34261         /** @private */
34262         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34263         this.el.addClass("roo-splitbar-v");
34264     }
34265     
34266     this.addEvents({
34267         /**
34268          * @event resize
34269          * Fires when the splitter is moved (alias for {@link #event-moved})
34270          * @param {Roo.bootstrap.SplitBar} this
34271          * @param {Number} newSize the new width or height
34272          */
34273         "resize" : true,
34274         /**
34275          * @event moved
34276          * Fires when the splitter is moved
34277          * @param {Roo.bootstrap.SplitBar} this
34278          * @param {Number} newSize the new width or height
34279          */
34280         "moved" : true,
34281         /**
34282          * @event beforeresize
34283          * Fires before the splitter is dragged
34284          * @param {Roo.bootstrap.SplitBar} this
34285          */
34286         "beforeresize" : true,
34287
34288         "beforeapply" : true
34289     });
34290
34291     Roo.util.Observable.call(this);
34292 };
34293
34294 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34295     onStartProxyDrag : function(x, y){
34296         this.fireEvent("beforeresize", this);
34297         if(!this.overlay){
34298             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34299             o.unselectable();
34300             o.enableDisplayMode("block");
34301             // all splitbars share the same overlay
34302             Roo.bootstrap.SplitBar.prototype.overlay = o;
34303         }
34304         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34305         this.overlay.show();
34306         Roo.get(this.proxy).setDisplayed("block");
34307         var size = this.adapter.getElementSize(this);
34308         this.activeMinSize = this.getMinimumSize();;
34309         this.activeMaxSize = this.getMaximumSize();;
34310         var c1 = size - this.activeMinSize;
34311         var c2 = Math.max(this.activeMaxSize - size, 0);
34312         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34313             this.dd.resetConstraints();
34314             this.dd.setXConstraint(
34315                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34316                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34317             );
34318             this.dd.setYConstraint(0, 0);
34319         }else{
34320             this.dd.resetConstraints();
34321             this.dd.setXConstraint(0, 0);
34322             this.dd.setYConstraint(
34323                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34324                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34325             );
34326          }
34327         this.dragSpecs.startSize = size;
34328         this.dragSpecs.startPoint = [x, y];
34329         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34330     },
34331     
34332     /** 
34333      * @private Called after the drag operation by the DDProxy
34334      */
34335     onEndProxyDrag : function(e){
34336         Roo.get(this.proxy).setDisplayed(false);
34337         var endPoint = Roo.lib.Event.getXY(e);
34338         if(this.overlay){
34339             this.overlay.hide();
34340         }
34341         var newSize;
34342         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34343             newSize = this.dragSpecs.startSize + 
34344                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34345                     endPoint[0] - this.dragSpecs.startPoint[0] :
34346                     this.dragSpecs.startPoint[0] - endPoint[0]
34347                 );
34348         }else{
34349             newSize = this.dragSpecs.startSize + 
34350                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34351                     endPoint[1] - this.dragSpecs.startPoint[1] :
34352                     this.dragSpecs.startPoint[1] - endPoint[1]
34353                 );
34354         }
34355         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34356         if(newSize != this.dragSpecs.startSize){
34357             if(this.fireEvent('beforeapply', this, newSize) !== false){
34358                 this.adapter.setElementSize(this, newSize);
34359                 this.fireEvent("moved", this, newSize);
34360                 this.fireEvent("resize", this, newSize);
34361             }
34362         }
34363     },
34364     
34365     /**
34366      * Get the adapter this SplitBar uses
34367      * @return The adapter object
34368      */
34369     getAdapter : function(){
34370         return this.adapter;
34371     },
34372     
34373     /**
34374      * Set the adapter this SplitBar uses
34375      * @param {Object} adapter A SplitBar adapter object
34376      */
34377     setAdapter : function(adapter){
34378         this.adapter = adapter;
34379         this.adapter.init(this);
34380     },
34381     
34382     /**
34383      * Gets the minimum size for the resizing element
34384      * @return {Number} The minimum size
34385      */
34386     getMinimumSize : function(){
34387         return this.minSize;
34388     },
34389     
34390     /**
34391      * Sets the minimum size for the resizing element
34392      * @param {Number} minSize The minimum size
34393      */
34394     setMinimumSize : function(minSize){
34395         this.minSize = minSize;
34396     },
34397     
34398     /**
34399      * Gets the maximum size for the resizing element
34400      * @return {Number} The maximum size
34401      */
34402     getMaximumSize : function(){
34403         return this.maxSize;
34404     },
34405     
34406     /**
34407      * Sets the maximum size for the resizing element
34408      * @param {Number} maxSize The maximum size
34409      */
34410     setMaximumSize : function(maxSize){
34411         this.maxSize = maxSize;
34412     },
34413     
34414     /**
34415      * Sets the initialize size for the resizing element
34416      * @param {Number} size The initial size
34417      */
34418     setCurrentSize : function(size){
34419         var oldAnimate = this.animate;
34420         this.animate = false;
34421         this.adapter.setElementSize(this, size);
34422         this.animate = oldAnimate;
34423     },
34424     
34425     /**
34426      * Destroy this splitbar. 
34427      * @param {Boolean} removeEl True to remove the element
34428      */
34429     destroy : function(removeEl){
34430         if(this.shim){
34431             this.shim.remove();
34432         }
34433         this.dd.unreg();
34434         this.proxy.parentNode.removeChild(this.proxy);
34435         if(removeEl){
34436             this.el.remove();
34437         }
34438     }
34439 });
34440
34441 /**
34442  * @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.
34443  */
34444 Roo.bootstrap.SplitBar.createProxy = function(dir){
34445     var proxy = new Roo.Element(document.createElement("div"));
34446     proxy.unselectable();
34447     var cls = 'roo-splitbar-proxy';
34448     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34449     document.body.appendChild(proxy.dom);
34450     return proxy.dom;
34451 };
34452
34453 /** 
34454  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34455  * Default Adapter. It assumes the splitter and resizing element are not positioned
34456  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34457  */
34458 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34459 };
34460
34461 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34462     // do nothing for now
34463     init : function(s){
34464     
34465     },
34466     /**
34467      * Called before drag operations to get the current size of the resizing element. 
34468      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34469      */
34470      getElementSize : function(s){
34471         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34472             return s.resizingEl.getWidth();
34473         }else{
34474             return s.resizingEl.getHeight();
34475         }
34476     },
34477     
34478     /**
34479      * Called after drag operations to set the size of the resizing element.
34480      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34481      * @param {Number} newSize The new size to set
34482      * @param {Function} onComplete A function to be invoked when resizing is complete
34483      */
34484     setElementSize : function(s, newSize, onComplete){
34485         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34486             if(!s.animate){
34487                 s.resizingEl.setWidth(newSize);
34488                 if(onComplete){
34489                     onComplete(s, newSize);
34490                 }
34491             }else{
34492                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34493             }
34494         }else{
34495             
34496             if(!s.animate){
34497                 s.resizingEl.setHeight(newSize);
34498                 if(onComplete){
34499                     onComplete(s, newSize);
34500                 }
34501             }else{
34502                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34503             }
34504         }
34505     }
34506 };
34507
34508 /** 
34509  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34510  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34511  * Adapter that  moves the splitter element to align with the resized sizing element. 
34512  * Used with an absolute positioned SplitBar.
34513  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34514  * document.body, make sure you assign an id to the body element.
34515  */
34516 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34517     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34518     this.container = Roo.get(container);
34519 };
34520
34521 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34522     init : function(s){
34523         this.basic.init(s);
34524     },
34525     
34526     getElementSize : function(s){
34527         return this.basic.getElementSize(s);
34528     },
34529     
34530     setElementSize : function(s, newSize, onComplete){
34531         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34532     },
34533     
34534     moveSplitter : function(s){
34535         var yes = Roo.bootstrap.SplitBar;
34536         switch(s.placement){
34537             case yes.LEFT:
34538                 s.el.setX(s.resizingEl.getRight());
34539                 break;
34540             case yes.RIGHT:
34541                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34542                 break;
34543             case yes.TOP:
34544                 s.el.setY(s.resizingEl.getBottom());
34545                 break;
34546             case yes.BOTTOM:
34547                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34548                 break;
34549         }
34550     }
34551 };
34552
34553 /**
34554  * Orientation constant - Create a vertical SplitBar
34555  * @static
34556  * @type Number
34557  */
34558 Roo.bootstrap.SplitBar.VERTICAL = 1;
34559
34560 /**
34561  * Orientation constant - Create a horizontal SplitBar
34562  * @static
34563  * @type Number
34564  */
34565 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34566
34567 /**
34568  * Placement constant - The resizing element is to the left of the splitter element
34569  * @static
34570  * @type Number
34571  */
34572 Roo.bootstrap.SplitBar.LEFT = 1;
34573
34574 /**
34575  * Placement constant - The resizing element is to the right of the splitter element
34576  * @static
34577  * @type Number
34578  */
34579 Roo.bootstrap.SplitBar.RIGHT = 2;
34580
34581 /**
34582  * Placement constant - The resizing element is positioned above the splitter element
34583  * @static
34584  * @type Number
34585  */
34586 Roo.bootstrap.SplitBar.TOP = 3;
34587
34588 /**
34589  * Placement constant - The resizing element is positioned under splitter element
34590  * @static
34591  * @type Number
34592  */
34593 Roo.bootstrap.SplitBar.BOTTOM = 4;
34594 Roo.namespace("Roo.bootstrap.layout");/*
34595  * Based on:
34596  * Ext JS Library 1.1.1
34597  * Copyright(c) 2006-2007, Ext JS, LLC.
34598  *
34599  * Originally Released Under LGPL - original licence link has changed is not relivant.
34600  *
34601  * Fork - LGPL
34602  * <script type="text/javascript">
34603  */
34604
34605 /**
34606  * @class Roo.bootstrap.layout.Manager
34607  * @extends Roo.bootstrap.Component
34608  * Base class for layout managers.
34609  */
34610 Roo.bootstrap.layout.Manager = function(config)
34611 {
34612     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34613
34614
34615
34616
34617
34618     /** false to disable window resize monitoring @type Boolean */
34619     this.monitorWindowResize = true;
34620     this.regions = {};
34621     this.addEvents({
34622         /**
34623          * @event layout
34624          * Fires when a layout is performed.
34625          * @param {Roo.LayoutManager} this
34626          */
34627         "layout" : true,
34628         /**
34629          * @event regionresized
34630          * Fires when the user resizes a region.
34631          * @param {Roo.LayoutRegion} region The resized region
34632          * @param {Number} newSize The new size (width for east/west, height for north/south)
34633          */
34634         "regionresized" : true,
34635         /**
34636          * @event regioncollapsed
34637          * Fires when a region is collapsed.
34638          * @param {Roo.LayoutRegion} region The collapsed region
34639          */
34640         "regioncollapsed" : true,
34641         /**
34642          * @event regionexpanded
34643          * Fires when a region is expanded.
34644          * @param {Roo.LayoutRegion} region The expanded region
34645          */
34646         "regionexpanded" : true
34647     });
34648     this.updating = false;
34649
34650     if (config.el) {
34651         this.el = Roo.get(config.el);
34652         this.initEvents();
34653     }
34654
34655 };
34656
34657 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34658
34659
34660     regions : null,
34661
34662     monitorWindowResize : true,
34663
34664
34665     updating : false,
34666
34667
34668     onRender : function(ct, position)
34669     {
34670         if(!this.el){
34671             this.el = Roo.get(ct);
34672             this.initEvents();
34673         }
34674         //this.fireEvent('render',this);
34675     },
34676
34677
34678     initEvents: function()
34679     {
34680
34681
34682         // ie scrollbar fix
34683         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34684             document.body.scroll = "no";
34685         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34686             this.el.position('relative');
34687         }
34688         this.id = this.el.id;
34689         this.el.addClass("roo-layout-container");
34690         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34691         if(this.el.dom != document.body ) {
34692             this.el.on('resize', this.layout,this);
34693             this.el.on('show', this.layout,this);
34694         }
34695
34696     },
34697
34698     /**
34699      * Returns true if this layout is currently being updated
34700      * @return {Boolean}
34701      */
34702     isUpdating : function(){
34703         return this.updating;
34704     },
34705
34706     /**
34707      * Suspend the LayoutManager from doing auto-layouts while
34708      * making multiple add or remove calls
34709      */
34710     beginUpdate : function(){
34711         this.updating = true;
34712     },
34713
34714     /**
34715      * Restore auto-layouts and optionally disable the manager from performing a layout
34716      * @param {Boolean} noLayout true to disable a layout update
34717      */
34718     endUpdate : function(noLayout){
34719         this.updating = false;
34720         if(!noLayout){
34721             this.layout();
34722         }
34723     },
34724
34725     layout: function(){
34726         // abstract...
34727     },
34728
34729     onRegionResized : function(region, newSize){
34730         this.fireEvent("regionresized", region, newSize);
34731         this.layout();
34732     },
34733
34734     onRegionCollapsed : function(region){
34735         this.fireEvent("regioncollapsed", region);
34736     },
34737
34738     onRegionExpanded : function(region){
34739         this.fireEvent("regionexpanded", region);
34740     },
34741
34742     /**
34743      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34744      * performs box-model adjustments.
34745      * @return {Object} The size as an object {width: (the width), height: (the height)}
34746      */
34747     getViewSize : function()
34748     {
34749         var size;
34750         if(this.el.dom != document.body){
34751             size = this.el.getSize();
34752         }else{
34753             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34754         }
34755         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34756         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34757         return size;
34758     },
34759
34760     /**
34761      * Returns the Element this layout is bound to.
34762      * @return {Roo.Element}
34763      */
34764     getEl : function(){
34765         return this.el;
34766     },
34767
34768     /**
34769      * Returns the specified region.
34770      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34771      * @return {Roo.LayoutRegion}
34772      */
34773     getRegion : function(target){
34774         return this.regions[target.toLowerCase()];
34775     },
34776
34777     onWindowResize : function(){
34778         if(this.monitorWindowResize){
34779             this.layout();
34780         }
34781     }
34782 });
34783 /*
34784  * Based on:
34785  * Ext JS Library 1.1.1
34786  * Copyright(c) 2006-2007, Ext JS, LLC.
34787  *
34788  * Originally Released Under LGPL - original licence link has changed is not relivant.
34789  *
34790  * Fork - LGPL
34791  * <script type="text/javascript">
34792  */
34793 /**
34794  * @class Roo.bootstrap.layout.Border
34795  * @extends Roo.bootstrap.layout.Manager
34796  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34797  * please see: examples/bootstrap/nested.html<br><br>
34798  
34799 <b>The container the layout is rendered into can be either the body element or any other element.
34800 If it is not the body element, the container needs to either be an absolute positioned element,
34801 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34802 the container size if it is not the body element.</b>
34803
34804 * @constructor
34805 * Create a new Border
34806 * @param {Object} config Configuration options
34807  */
34808 Roo.bootstrap.layout.Border = function(config){
34809     config = config || {};
34810     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34811     
34812     
34813     
34814     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34815         if(config[region]){
34816             config[region].region = region;
34817             this.addRegion(config[region]);
34818         }
34819     },this);
34820     
34821 };
34822
34823 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34824
34825 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34826     /**
34827      * Creates and adds a new region if it doesn't already exist.
34828      * @param {String} target The target region key (north, south, east, west or center).
34829      * @param {Object} config The regions config object
34830      * @return {BorderLayoutRegion} The new region
34831      */
34832     addRegion : function(config)
34833     {
34834         if(!this.regions[config.region]){
34835             var r = this.factory(config);
34836             this.bindRegion(r);
34837         }
34838         return this.regions[config.region];
34839     },
34840
34841     // private (kinda)
34842     bindRegion : function(r){
34843         this.regions[r.config.region] = r;
34844         
34845         r.on("visibilitychange",    this.layout, this);
34846         r.on("paneladded",          this.layout, this);
34847         r.on("panelremoved",        this.layout, this);
34848         r.on("invalidated",         this.layout, this);
34849         r.on("resized",             this.onRegionResized, this);
34850         r.on("collapsed",           this.onRegionCollapsed, this);
34851         r.on("expanded",            this.onRegionExpanded, this);
34852     },
34853
34854     /**
34855      * Performs a layout update.
34856      */
34857     layout : function()
34858     {
34859         if(this.updating) {
34860             return;
34861         }
34862         
34863         // render all the rebions if they have not been done alreayd?
34864         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34865             if(this.regions[region] && !this.regions[region].bodyEl){
34866                 this.regions[region].onRender(this.el)
34867             }
34868         },this);
34869         
34870         var size = this.getViewSize();
34871         var w = size.width;
34872         var h = size.height;
34873         var centerW = w;
34874         var centerH = h;
34875         var centerY = 0;
34876         var centerX = 0;
34877         //var x = 0, y = 0;
34878
34879         var rs = this.regions;
34880         var north = rs["north"];
34881         var south = rs["south"]; 
34882         var west = rs["west"];
34883         var east = rs["east"];
34884         var center = rs["center"];
34885         //if(this.hideOnLayout){ // not supported anymore
34886             //c.el.setStyle("display", "none");
34887         //}
34888         if(north && north.isVisible()){
34889             var b = north.getBox();
34890             var m = north.getMargins();
34891             b.width = w - (m.left+m.right);
34892             b.x = m.left;
34893             b.y = m.top;
34894             centerY = b.height + b.y + m.bottom;
34895             centerH -= centerY;
34896             north.updateBox(this.safeBox(b));
34897         }
34898         if(south && south.isVisible()){
34899             var b = south.getBox();
34900             var m = south.getMargins();
34901             b.width = w - (m.left+m.right);
34902             b.x = m.left;
34903             var totalHeight = (b.height + m.top + m.bottom);
34904             b.y = h - totalHeight + m.top;
34905             centerH -= totalHeight;
34906             south.updateBox(this.safeBox(b));
34907         }
34908         if(west && west.isVisible()){
34909             var b = west.getBox();
34910             var m = west.getMargins();
34911             b.height = centerH - (m.top+m.bottom);
34912             b.x = m.left;
34913             b.y = centerY + m.top;
34914             var totalWidth = (b.width + m.left + m.right);
34915             centerX += totalWidth;
34916             centerW -= totalWidth;
34917             west.updateBox(this.safeBox(b));
34918         }
34919         if(east && east.isVisible()){
34920             var b = east.getBox();
34921             var m = east.getMargins();
34922             b.height = centerH - (m.top+m.bottom);
34923             var totalWidth = (b.width + m.left + m.right);
34924             b.x = w - totalWidth + m.left;
34925             b.y = centerY + m.top;
34926             centerW -= totalWidth;
34927             east.updateBox(this.safeBox(b));
34928         }
34929         if(center){
34930             var m = center.getMargins();
34931             var centerBox = {
34932                 x: centerX + m.left,
34933                 y: centerY + m.top,
34934                 width: centerW - (m.left+m.right),
34935                 height: centerH - (m.top+m.bottom)
34936             };
34937             //if(this.hideOnLayout){
34938                 //center.el.setStyle("display", "block");
34939             //}
34940             center.updateBox(this.safeBox(centerBox));
34941         }
34942         this.el.repaint();
34943         this.fireEvent("layout", this);
34944     },
34945
34946     // private
34947     safeBox : function(box){
34948         box.width = Math.max(0, box.width);
34949         box.height = Math.max(0, box.height);
34950         return box;
34951     },
34952
34953     /**
34954      * Adds a ContentPanel (or subclass) to this layout.
34955      * @param {String} target The target region key (north, south, east, west or center).
34956      * @param {Roo.ContentPanel} panel The panel to add
34957      * @return {Roo.ContentPanel} The added panel
34958      */
34959     add : function(target, panel){
34960          
34961         target = target.toLowerCase();
34962         return this.regions[target].add(panel);
34963     },
34964
34965     /**
34966      * Remove a ContentPanel (or subclass) to this layout.
34967      * @param {String} target The target region key (north, south, east, west or center).
34968      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34969      * @return {Roo.ContentPanel} The removed panel
34970      */
34971     remove : function(target, panel){
34972         target = target.toLowerCase();
34973         return this.regions[target].remove(panel);
34974     },
34975
34976     /**
34977      * Searches all regions for a panel with the specified id
34978      * @param {String} panelId
34979      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34980      */
34981     findPanel : function(panelId){
34982         var rs = this.regions;
34983         for(var target in rs){
34984             if(typeof rs[target] != "function"){
34985                 var p = rs[target].getPanel(panelId);
34986                 if(p){
34987                     return p;
34988                 }
34989             }
34990         }
34991         return null;
34992     },
34993
34994     /**
34995      * Searches all regions for a panel with the specified id and activates (shows) it.
34996      * @param {String/ContentPanel} panelId The panels id or the panel itself
34997      * @return {Roo.ContentPanel} The shown panel or null
34998      */
34999     showPanel : function(panelId) {
35000       var rs = this.regions;
35001       for(var target in rs){
35002          var r = rs[target];
35003          if(typeof r != "function"){
35004             if(r.hasPanel(panelId)){
35005                return r.showPanel(panelId);
35006             }
35007          }
35008       }
35009       return null;
35010    },
35011
35012    /**
35013      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35014      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35015      */
35016    /*
35017     restoreState : function(provider){
35018         if(!provider){
35019             provider = Roo.state.Manager;
35020         }
35021         var sm = new Roo.LayoutStateManager();
35022         sm.init(this, provider);
35023     },
35024 */
35025  
35026  
35027     /**
35028      * Adds a xtype elements to the layout.
35029      * <pre><code>
35030
35031 layout.addxtype({
35032        xtype : 'ContentPanel',
35033        region: 'west',
35034        items: [ .... ]
35035    }
35036 );
35037
35038 layout.addxtype({
35039         xtype : 'NestedLayoutPanel',
35040         region: 'west',
35041         layout: {
35042            center: { },
35043            west: { }   
35044         },
35045         items : [ ... list of content panels or nested layout panels.. ]
35046    }
35047 );
35048 </code></pre>
35049      * @param {Object} cfg Xtype definition of item to add.
35050      */
35051     addxtype : function(cfg)
35052     {
35053         // basically accepts a pannel...
35054         // can accept a layout region..!?!?
35055         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35056         
35057         
35058         // theory?  children can only be panels??
35059         
35060         //if (!cfg.xtype.match(/Panel$/)) {
35061         //    return false;
35062         //}
35063         var ret = false;
35064         
35065         if (typeof(cfg.region) == 'undefined') {
35066             Roo.log("Failed to add Panel, region was not set");
35067             Roo.log(cfg);
35068             return false;
35069         }
35070         var region = cfg.region;
35071         delete cfg.region;
35072         
35073           
35074         var xitems = [];
35075         if (cfg.items) {
35076             xitems = cfg.items;
35077             delete cfg.items;
35078         }
35079         var nb = false;
35080         
35081         switch(cfg.xtype) 
35082         {
35083             case 'Content':  // ContentPanel (el, cfg)
35084             case 'Scroll':  // ContentPanel (el, cfg)
35085             case 'View': 
35086                 cfg.autoCreate = true;
35087                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35088                 //} else {
35089                 //    var el = this.el.createChild();
35090                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35091                 //}
35092                 
35093                 this.add(region, ret);
35094                 break;
35095             
35096             /*
35097             case 'TreePanel': // our new panel!
35098                 cfg.el = this.el.createChild();
35099                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35100                 this.add(region, ret);
35101                 break;
35102             */
35103             
35104             case 'Nest': 
35105                 // create a new Layout (which is  a Border Layout...
35106                 
35107                 var clayout = cfg.layout;
35108                 clayout.el  = this.el.createChild();
35109                 clayout.items   = clayout.items  || [];
35110                 
35111                 delete cfg.layout;
35112                 
35113                 // replace this exitems with the clayout ones..
35114                 xitems = clayout.items;
35115                  
35116                 // force background off if it's in center...
35117                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35118                     cfg.background = false;
35119                 }
35120                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35121                 
35122                 
35123                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35124                 //console.log('adding nested layout panel '  + cfg.toSource());
35125                 this.add(region, ret);
35126                 nb = {}; /// find first...
35127                 break;
35128             
35129             case 'Grid':
35130                 
35131                 // needs grid and region
35132                 
35133                 //var el = this.getRegion(region).el.createChild();
35134                 /*
35135                  *var el = this.el.createChild();
35136                 // create the grid first...
35137                 cfg.grid.container = el;
35138                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35139                 */
35140                 
35141                 if (region == 'center' && this.active ) {
35142                     cfg.background = false;
35143                 }
35144                 
35145                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35146                 
35147                 this.add(region, ret);
35148                 /*
35149                 if (cfg.background) {
35150                     // render grid on panel activation (if panel background)
35151                     ret.on('activate', function(gp) {
35152                         if (!gp.grid.rendered) {
35153                     //        gp.grid.render(el);
35154                         }
35155                     });
35156                 } else {
35157                   //  cfg.grid.render(el);
35158                 }
35159                 */
35160                 break;
35161            
35162            
35163             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35164                 // it was the old xcomponent building that caused this before.
35165                 // espeically if border is the top element in the tree.
35166                 ret = this;
35167                 break; 
35168                 
35169                     
35170                 
35171                 
35172                 
35173             default:
35174                 /*
35175                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35176                     
35177                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35178                     this.add(region, ret);
35179                 } else {
35180                 */
35181                     Roo.log(cfg);
35182                     throw "Can not add '" + cfg.xtype + "' to Border";
35183                     return null;
35184              
35185                                 
35186              
35187         }
35188         this.beginUpdate();
35189         // add children..
35190         var region = '';
35191         var abn = {};
35192         Roo.each(xitems, function(i)  {
35193             region = nb && i.region ? i.region : false;
35194             
35195             var add = ret.addxtype(i);
35196            
35197             if (region) {
35198                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35199                 if (!i.background) {
35200                     abn[region] = nb[region] ;
35201                 }
35202             }
35203             
35204         });
35205         this.endUpdate();
35206
35207         // make the last non-background panel active..
35208         //if (nb) { Roo.log(abn); }
35209         if (nb) {
35210             
35211             for(var r in abn) {
35212                 region = this.getRegion(r);
35213                 if (region) {
35214                     // tried using nb[r], but it does not work..
35215                      
35216                     region.showPanel(abn[r]);
35217                    
35218                 }
35219             }
35220         }
35221         return ret;
35222         
35223     },
35224     
35225     
35226 // private
35227     factory : function(cfg)
35228     {
35229         
35230         var validRegions = Roo.bootstrap.layout.Border.regions;
35231
35232         var target = cfg.region;
35233         cfg.mgr = this;
35234         
35235         var r = Roo.bootstrap.layout;
35236         Roo.log(target);
35237         switch(target){
35238             case "north":
35239                 return new r.North(cfg);
35240             case "south":
35241                 return new r.South(cfg);
35242             case "east":
35243                 return new r.East(cfg);
35244             case "west":
35245                 return new r.West(cfg);
35246             case "center":
35247                 return new r.Center(cfg);
35248         }
35249         throw 'Layout region "'+target+'" not supported.';
35250     }
35251     
35252     
35253 });
35254  /*
35255  * Based on:
35256  * Ext JS Library 1.1.1
35257  * Copyright(c) 2006-2007, Ext JS, LLC.
35258  *
35259  * Originally Released Under LGPL - original licence link has changed is not relivant.
35260  *
35261  * Fork - LGPL
35262  * <script type="text/javascript">
35263  */
35264  
35265 /**
35266  * @class Roo.bootstrap.layout.Basic
35267  * @extends Roo.util.Observable
35268  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35269  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35270  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35271  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35272  * @cfg {string}   region  the region that it inhabits..
35273  * @cfg {bool}   skipConfig skip config?
35274  * 
35275
35276  */
35277 Roo.bootstrap.layout.Basic = function(config){
35278     
35279     this.mgr = config.mgr;
35280     
35281     this.position = config.region;
35282     
35283     var skipConfig = config.skipConfig;
35284     
35285     this.events = {
35286         /**
35287          * @scope Roo.BasicLayoutRegion
35288          */
35289         
35290         /**
35291          * @event beforeremove
35292          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35293          * @param {Roo.LayoutRegion} this
35294          * @param {Roo.ContentPanel} panel The panel
35295          * @param {Object} e The cancel event object
35296          */
35297         "beforeremove" : true,
35298         /**
35299          * @event invalidated
35300          * Fires when the layout for this region is changed.
35301          * @param {Roo.LayoutRegion} this
35302          */
35303         "invalidated" : true,
35304         /**
35305          * @event visibilitychange
35306          * Fires when this region is shown or hidden 
35307          * @param {Roo.LayoutRegion} this
35308          * @param {Boolean} visibility true or false
35309          */
35310         "visibilitychange" : true,
35311         /**
35312          * @event paneladded
35313          * Fires when a panel is added. 
35314          * @param {Roo.LayoutRegion} this
35315          * @param {Roo.ContentPanel} panel The panel
35316          */
35317         "paneladded" : true,
35318         /**
35319          * @event panelremoved
35320          * Fires when a panel is removed. 
35321          * @param {Roo.LayoutRegion} this
35322          * @param {Roo.ContentPanel} panel The panel
35323          */
35324         "panelremoved" : true,
35325         /**
35326          * @event beforecollapse
35327          * Fires when this region before collapse.
35328          * @param {Roo.LayoutRegion} this
35329          */
35330         "beforecollapse" : true,
35331         /**
35332          * @event collapsed
35333          * Fires when this region is collapsed.
35334          * @param {Roo.LayoutRegion} this
35335          */
35336         "collapsed" : true,
35337         /**
35338          * @event expanded
35339          * Fires when this region is expanded.
35340          * @param {Roo.LayoutRegion} this
35341          */
35342         "expanded" : true,
35343         /**
35344          * @event slideshow
35345          * Fires when this region is slid into view.
35346          * @param {Roo.LayoutRegion} this
35347          */
35348         "slideshow" : true,
35349         /**
35350          * @event slidehide
35351          * Fires when this region slides out of view. 
35352          * @param {Roo.LayoutRegion} this
35353          */
35354         "slidehide" : true,
35355         /**
35356          * @event panelactivated
35357          * Fires when a panel is activated. 
35358          * @param {Roo.LayoutRegion} this
35359          * @param {Roo.ContentPanel} panel The activated panel
35360          */
35361         "panelactivated" : true,
35362         /**
35363          * @event resized
35364          * Fires when the user resizes this region. 
35365          * @param {Roo.LayoutRegion} this
35366          * @param {Number} newSize The new size (width for east/west, height for north/south)
35367          */
35368         "resized" : true
35369     };
35370     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35371     this.panels = new Roo.util.MixedCollection();
35372     this.panels.getKey = this.getPanelId.createDelegate(this);
35373     this.box = null;
35374     this.activePanel = null;
35375     // ensure listeners are added...
35376     
35377     if (config.listeners || config.events) {
35378         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35379             listeners : config.listeners || {},
35380             events : config.events || {}
35381         });
35382     }
35383     
35384     if(skipConfig !== true){
35385         this.applyConfig(config);
35386     }
35387 };
35388
35389 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35390 {
35391     getPanelId : function(p){
35392         return p.getId();
35393     },
35394     
35395     applyConfig : function(config){
35396         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35397         this.config = config;
35398         
35399     },
35400     
35401     /**
35402      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35403      * the width, for horizontal (north, south) the height.
35404      * @param {Number} newSize The new width or height
35405      */
35406     resizeTo : function(newSize){
35407         var el = this.el ? this.el :
35408                  (this.activePanel ? this.activePanel.getEl() : null);
35409         if(el){
35410             switch(this.position){
35411                 case "east":
35412                 case "west":
35413                     el.setWidth(newSize);
35414                     this.fireEvent("resized", this, newSize);
35415                 break;
35416                 case "north":
35417                 case "south":
35418                     el.setHeight(newSize);
35419                     this.fireEvent("resized", this, newSize);
35420                 break;                
35421             }
35422         }
35423     },
35424     
35425     getBox : function(){
35426         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35427     },
35428     
35429     getMargins : function(){
35430         return this.margins;
35431     },
35432     
35433     updateBox : function(box){
35434         this.box = box;
35435         var el = this.activePanel.getEl();
35436         el.dom.style.left = box.x + "px";
35437         el.dom.style.top = box.y + "px";
35438         this.activePanel.setSize(box.width, box.height);
35439     },
35440     
35441     /**
35442      * Returns the container element for this region.
35443      * @return {Roo.Element}
35444      */
35445     getEl : function(){
35446         return this.activePanel;
35447     },
35448     
35449     /**
35450      * Returns true if this region is currently visible.
35451      * @return {Boolean}
35452      */
35453     isVisible : function(){
35454         return this.activePanel ? true : false;
35455     },
35456     
35457     setActivePanel : function(panel){
35458         panel = this.getPanel(panel);
35459         if(this.activePanel && this.activePanel != panel){
35460             this.activePanel.setActiveState(false);
35461             this.activePanel.getEl().setLeftTop(-10000,-10000);
35462         }
35463         this.activePanel = panel;
35464         panel.setActiveState(true);
35465         if(this.box){
35466             panel.setSize(this.box.width, this.box.height);
35467         }
35468         this.fireEvent("panelactivated", this, panel);
35469         this.fireEvent("invalidated");
35470     },
35471     
35472     /**
35473      * Show the specified panel.
35474      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35475      * @return {Roo.ContentPanel} The shown panel or null
35476      */
35477     showPanel : function(panel){
35478         panel = this.getPanel(panel);
35479         if(panel){
35480             this.setActivePanel(panel);
35481         }
35482         return panel;
35483     },
35484     
35485     /**
35486      * Get the active panel for this region.
35487      * @return {Roo.ContentPanel} The active panel or null
35488      */
35489     getActivePanel : function(){
35490         return this.activePanel;
35491     },
35492     
35493     /**
35494      * Add the passed ContentPanel(s)
35495      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35496      * @return {Roo.ContentPanel} The panel added (if only one was added)
35497      */
35498     add : function(panel){
35499         if(arguments.length > 1){
35500             for(var i = 0, len = arguments.length; i < len; i++) {
35501                 this.add(arguments[i]);
35502             }
35503             return null;
35504         }
35505         if(this.hasPanel(panel)){
35506             this.showPanel(panel);
35507             return panel;
35508         }
35509         var el = panel.getEl();
35510         if(el.dom.parentNode != this.mgr.el.dom){
35511             this.mgr.el.dom.appendChild(el.dom);
35512         }
35513         if(panel.setRegion){
35514             panel.setRegion(this);
35515         }
35516         this.panels.add(panel);
35517         el.setStyle("position", "absolute");
35518         if(!panel.background){
35519             this.setActivePanel(panel);
35520             if(this.config.initialSize && this.panels.getCount()==1){
35521                 this.resizeTo(this.config.initialSize);
35522             }
35523         }
35524         this.fireEvent("paneladded", this, panel);
35525         return panel;
35526     },
35527     
35528     /**
35529      * Returns true if the panel is in this region.
35530      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35531      * @return {Boolean}
35532      */
35533     hasPanel : function(panel){
35534         if(typeof panel == "object"){ // must be panel obj
35535             panel = panel.getId();
35536         }
35537         return this.getPanel(panel) ? true : false;
35538     },
35539     
35540     /**
35541      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35542      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35543      * @param {Boolean} preservePanel Overrides the config preservePanel option
35544      * @return {Roo.ContentPanel} The panel that was removed
35545      */
35546     remove : function(panel, preservePanel){
35547         panel = this.getPanel(panel);
35548         if(!panel){
35549             return null;
35550         }
35551         var e = {};
35552         this.fireEvent("beforeremove", this, panel, e);
35553         if(e.cancel === true){
35554             return null;
35555         }
35556         var panelId = panel.getId();
35557         this.panels.removeKey(panelId);
35558         return panel;
35559     },
35560     
35561     /**
35562      * Returns the panel specified or null if it's not in this region.
35563      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35564      * @return {Roo.ContentPanel}
35565      */
35566     getPanel : function(id){
35567         if(typeof id == "object"){ // must be panel obj
35568             return id;
35569         }
35570         return this.panels.get(id);
35571     },
35572     
35573     /**
35574      * Returns this regions position (north/south/east/west/center).
35575      * @return {String} 
35576      */
35577     getPosition: function(){
35578         return this.position;    
35579     }
35580 });/*
35581  * Based on:
35582  * Ext JS Library 1.1.1
35583  * Copyright(c) 2006-2007, Ext JS, LLC.
35584  *
35585  * Originally Released Under LGPL - original licence link has changed is not relivant.
35586  *
35587  * Fork - LGPL
35588  * <script type="text/javascript">
35589  */
35590  
35591 /**
35592  * @class Roo.bootstrap.layout.Region
35593  * @extends Roo.bootstrap.layout.Basic
35594  * This class represents a region in a layout manager.
35595  
35596  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35597  * @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})
35598  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35599  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35600  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35601  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35602  * @cfg {String}    title           The title for the region (overrides panel titles)
35603  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35604  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35605  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35606  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35607  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35608  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35609  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35610  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35611  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35612  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35613
35614  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35615  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35616  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35617  * @cfg {Number}    width           For East/West panels
35618  * @cfg {Number}    height          For North/South panels
35619  * @cfg {Boolean}   split           To show the splitter
35620  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35621  * 
35622  * @cfg {string}   cls             Extra CSS classes to add to region
35623  * 
35624  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35625  * @cfg {string}   region  the region that it inhabits..
35626  *
35627
35628  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35629  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35630
35631  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35632  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35633  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35634  */
35635 Roo.bootstrap.layout.Region = function(config)
35636 {
35637     this.applyConfig(config);
35638
35639     var mgr = config.mgr;
35640     var pos = config.region;
35641     config.skipConfig = true;
35642     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35643     
35644     if (mgr.el) {
35645         this.onRender(mgr.el);   
35646     }
35647      
35648     this.visible = true;
35649     this.collapsed = false;
35650     this.unrendered_panels = [];
35651 };
35652
35653 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35654
35655     position: '', // set by wrapper (eg. north/south etc..)
35656     unrendered_panels : null,  // unrendered panels.
35657     createBody : function(){
35658         /** This region's body element 
35659         * @type Roo.Element */
35660         this.bodyEl = this.el.createChild({
35661                 tag: "div",
35662                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35663         });
35664     },
35665
35666     onRender: function(ctr, pos)
35667     {
35668         var dh = Roo.DomHelper;
35669         /** This region's container element 
35670         * @type Roo.Element */
35671         this.el = dh.append(ctr.dom, {
35672                 tag: "div",
35673                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35674             }, true);
35675         /** This region's title element 
35676         * @type Roo.Element */
35677     
35678         this.titleEl = dh.append(this.el.dom,
35679             {
35680                     tag: "div",
35681                     unselectable: "on",
35682                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35683                     children:[
35684                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35685                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35686                     ]}, true);
35687         
35688         this.titleEl.enableDisplayMode();
35689         /** This region's title text element 
35690         * @type HTMLElement */
35691         this.titleTextEl = this.titleEl.dom.firstChild;
35692         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35693         /*
35694         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35695         this.closeBtn.enableDisplayMode();
35696         this.closeBtn.on("click", this.closeClicked, this);
35697         this.closeBtn.hide();
35698     */
35699         this.createBody(this.config);
35700         if(this.config.hideWhenEmpty){
35701             this.hide();
35702             this.on("paneladded", this.validateVisibility, this);
35703             this.on("panelremoved", this.validateVisibility, this);
35704         }
35705         if(this.autoScroll){
35706             this.bodyEl.setStyle("overflow", "auto");
35707         }else{
35708             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35709         }
35710         //if(c.titlebar !== false){
35711             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35712                 this.titleEl.hide();
35713             }else{
35714                 this.titleEl.show();
35715                 if(this.config.title){
35716                     this.titleTextEl.innerHTML = this.config.title;
35717                 }
35718             }
35719         //}
35720         if(this.config.collapsed){
35721             this.collapse(true);
35722         }
35723         if(this.config.hidden){
35724             this.hide();
35725         }
35726         
35727         if (this.unrendered_panels && this.unrendered_panels.length) {
35728             for (var i =0;i< this.unrendered_panels.length; i++) {
35729                 this.add(this.unrendered_panels[i]);
35730             }
35731             this.unrendered_panels = null;
35732             
35733         }
35734         
35735     },
35736     
35737     applyConfig : function(c)
35738     {
35739         /*
35740          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35741             var dh = Roo.DomHelper;
35742             if(c.titlebar !== false){
35743                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35744                 this.collapseBtn.on("click", this.collapse, this);
35745                 this.collapseBtn.enableDisplayMode();
35746                 /*
35747                 if(c.showPin === true || this.showPin){
35748                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35749                     this.stickBtn.enableDisplayMode();
35750                     this.stickBtn.on("click", this.expand, this);
35751                     this.stickBtn.hide();
35752                 }
35753                 
35754             }
35755             */
35756             /** This region's collapsed element
35757             * @type Roo.Element */
35758             /*
35759              *
35760             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35761                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35762             ]}, true);
35763             
35764             if(c.floatable !== false){
35765                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35766                this.collapsedEl.on("click", this.collapseClick, this);
35767             }
35768
35769             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35770                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35771                    id: "message", unselectable: "on", style:{"float":"left"}});
35772                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35773              }
35774             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35775             this.expandBtn.on("click", this.expand, this);
35776             
35777         }
35778         
35779         if(this.collapseBtn){
35780             this.collapseBtn.setVisible(c.collapsible == true);
35781         }
35782         
35783         this.cmargins = c.cmargins || this.cmargins ||
35784                          (this.position == "west" || this.position == "east" ?
35785                              {top: 0, left: 2, right:2, bottom: 0} :
35786                              {top: 2, left: 0, right:0, bottom: 2});
35787         */
35788         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35789         
35790         
35791         this.bottomTabs = c.tabPosition != "top";
35792         
35793         this.autoScroll = c.autoScroll || false;
35794         
35795         
35796        
35797         
35798         this.duration = c.duration || .30;
35799         this.slideDuration = c.slideDuration || .45;
35800         this.config = c;
35801        
35802     },
35803     /**
35804      * Returns true if this region is currently visible.
35805      * @return {Boolean}
35806      */
35807     isVisible : function(){
35808         return this.visible;
35809     },
35810
35811     /**
35812      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35813      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35814      */
35815     //setCollapsedTitle : function(title){
35816     //    title = title || "&#160;";
35817      //   if(this.collapsedTitleTextEl){
35818       //      this.collapsedTitleTextEl.innerHTML = title;
35819        // }
35820     //},
35821
35822     getBox : function(){
35823         var b;
35824       //  if(!this.collapsed){
35825             b = this.el.getBox(false, true);
35826        // }else{
35827           //  b = this.collapsedEl.getBox(false, true);
35828         //}
35829         return b;
35830     },
35831
35832     getMargins : function(){
35833         return this.margins;
35834         //return this.collapsed ? this.cmargins : this.margins;
35835     },
35836 /*
35837     highlight : function(){
35838         this.el.addClass("x-layout-panel-dragover");
35839     },
35840
35841     unhighlight : function(){
35842         this.el.removeClass("x-layout-panel-dragover");
35843     },
35844 */
35845     updateBox : function(box)
35846     {
35847         if (!this.bodyEl) {
35848             return; // not rendered yet..
35849         }
35850         
35851         this.box = box;
35852         if(!this.collapsed){
35853             this.el.dom.style.left = box.x + "px";
35854             this.el.dom.style.top = box.y + "px";
35855             this.updateBody(box.width, box.height);
35856         }else{
35857             this.collapsedEl.dom.style.left = box.x + "px";
35858             this.collapsedEl.dom.style.top = box.y + "px";
35859             this.collapsedEl.setSize(box.width, box.height);
35860         }
35861         if(this.tabs){
35862             this.tabs.autoSizeTabs();
35863         }
35864     },
35865
35866     updateBody : function(w, h)
35867     {
35868         if(w !== null){
35869             this.el.setWidth(w);
35870             w -= this.el.getBorderWidth("rl");
35871             if(this.config.adjustments){
35872                 w += this.config.adjustments[0];
35873             }
35874         }
35875         if(h !== null && h > 0){
35876             this.el.setHeight(h);
35877             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35878             h -= this.el.getBorderWidth("tb");
35879             if(this.config.adjustments){
35880                 h += this.config.adjustments[1];
35881             }
35882             this.bodyEl.setHeight(h);
35883             if(this.tabs){
35884                 h = this.tabs.syncHeight(h);
35885             }
35886         }
35887         if(this.panelSize){
35888             w = w !== null ? w : this.panelSize.width;
35889             h = h !== null ? h : this.panelSize.height;
35890         }
35891         if(this.activePanel){
35892             var el = this.activePanel.getEl();
35893             w = w !== null ? w : el.getWidth();
35894             h = h !== null ? h : el.getHeight();
35895             this.panelSize = {width: w, height: h};
35896             this.activePanel.setSize(w, h);
35897         }
35898         if(Roo.isIE && this.tabs){
35899             this.tabs.el.repaint();
35900         }
35901     },
35902
35903     /**
35904      * Returns the container element for this region.
35905      * @return {Roo.Element}
35906      */
35907     getEl : function(){
35908         return this.el;
35909     },
35910
35911     /**
35912      * Hides this region.
35913      */
35914     hide : function(){
35915         //if(!this.collapsed){
35916             this.el.dom.style.left = "-2000px";
35917             this.el.hide();
35918         //}else{
35919          //   this.collapsedEl.dom.style.left = "-2000px";
35920          //   this.collapsedEl.hide();
35921        // }
35922         this.visible = false;
35923         this.fireEvent("visibilitychange", this, false);
35924     },
35925
35926     /**
35927      * Shows this region if it was previously hidden.
35928      */
35929     show : function(){
35930         //if(!this.collapsed){
35931             this.el.show();
35932         //}else{
35933         //    this.collapsedEl.show();
35934        // }
35935         this.visible = true;
35936         this.fireEvent("visibilitychange", this, true);
35937     },
35938 /*
35939     closeClicked : function(){
35940         if(this.activePanel){
35941             this.remove(this.activePanel);
35942         }
35943     },
35944
35945     collapseClick : function(e){
35946         if(this.isSlid){
35947            e.stopPropagation();
35948            this.slideIn();
35949         }else{
35950            e.stopPropagation();
35951            this.slideOut();
35952         }
35953     },
35954 */
35955     /**
35956      * Collapses this region.
35957      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35958      */
35959     /*
35960     collapse : function(skipAnim, skipCheck = false){
35961         if(this.collapsed) {
35962             return;
35963         }
35964         
35965         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35966             
35967             this.collapsed = true;
35968             if(this.split){
35969                 this.split.el.hide();
35970             }
35971             if(this.config.animate && skipAnim !== true){
35972                 this.fireEvent("invalidated", this);
35973                 this.animateCollapse();
35974             }else{
35975                 this.el.setLocation(-20000,-20000);
35976                 this.el.hide();
35977                 this.collapsedEl.show();
35978                 this.fireEvent("collapsed", this);
35979                 this.fireEvent("invalidated", this);
35980             }
35981         }
35982         
35983     },
35984 */
35985     animateCollapse : function(){
35986         // overridden
35987     },
35988
35989     /**
35990      * Expands this region if it was previously collapsed.
35991      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35992      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35993      */
35994     /*
35995     expand : function(e, skipAnim){
35996         if(e) {
35997             e.stopPropagation();
35998         }
35999         if(!this.collapsed || this.el.hasActiveFx()) {
36000             return;
36001         }
36002         if(this.isSlid){
36003             this.afterSlideIn();
36004             skipAnim = true;
36005         }
36006         this.collapsed = false;
36007         if(this.config.animate && skipAnim !== true){
36008             this.animateExpand();
36009         }else{
36010             this.el.show();
36011             if(this.split){
36012                 this.split.el.show();
36013             }
36014             this.collapsedEl.setLocation(-2000,-2000);
36015             this.collapsedEl.hide();
36016             this.fireEvent("invalidated", this);
36017             this.fireEvent("expanded", this);
36018         }
36019     },
36020 */
36021     animateExpand : function(){
36022         // overridden
36023     },
36024
36025     initTabs : function()
36026     {
36027         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36028         
36029         var ts = new Roo.bootstrap.panel.Tabs({
36030                 el: this.bodyEl.dom,
36031                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36032                 disableTooltips: this.config.disableTabTips,
36033                 toolbar : this.config.toolbar
36034             });
36035         
36036         if(this.config.hideTabs){
36037             ts.stripWrap.setDisplayed(false);
36038         }
36039         this.tabs = ts;
36040         ts.resizeTabs = this.config.resizeTabs === true;
36041         ts.minTabWidth = this.config.minTabWidth || 40;
36042         ts.maxTabWidth = this.config.maxTabWidth || 250;
36043         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36044         ts.monitorResize = false;
36045         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36046         ts.bodyEl.addClass('roo-layout-tabs-body');
36047         this.panels.each(this.initPanelAsTab, this);
36048     },
36049
36050     initPanelAsTab : function(panel){
36051         var ti = this.tabs.addTab(
36052             panel.getEl().id,
36053             panel.getTitle(),
36054             null,
36055             this.config.closeOnTab && panel.isClosable(),
36056             panel.tpl
36057         );
36058         if(panel.tabTip !== undefined){
36059             ti.setTooltip(panel.tabTip);
36060         }
36061         ti.on("activate", function(){
36062               this.setActivePanel(panel);
36063         }, this);
36064         
36065         if(this.config.closeOnTab){
36066             ti.on("beforeclose", function(t, e){
36067                 e.cancel = true;
36068                 this.remove(panel);
36069             }, this);
36070         }
36071         
36072         panel.tabItem = ti;
36073         
36074         return ti;
36075     },
36076
36077     updatePanelTitle : function(panel, title)
36078     {
36079         if(this.activePanel == panel){
36080             this.updateTitle(title);
36081         }
36082         if(this.tabs){
36083             var ti = this.tabs.getTab(panel.getEl().id);
36084             ti.setText(title);
36085             if(panel.tabTip !== undefined){
36086                 ti.setTooltip(panel.tabTip);
36087             }
36088         }
36089     },
36090
36091     updateTitle : function(title){
36092         if(this.titleTextEl && !this.config.title){
36093             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36094         }
36095     },
36096
36097     setActivePanel : function(panel)
36098     {
36099         panel = this.getPanel(panel);
36100         if(this.activePanel && this.activePanel != panel){
36101             if(this.activePanel.setActiveState(false) === false){
36102                 return;
36103             }
36104         }
36105         this.activePanel = panel;
36106         panel.setActiveState(true);
36107         if(this.panelSize){
36108             panel.setSize(this.panelSize.width, this.panelSize.height);
36109         }
36110         if(this.closeBtn){
36111             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36112         }
36113         this.updateTitle(panel.getTitle());
36114         if(this.tabs){
36115             this.fireEvent("invalidated", this);
36116         }
36117         this.fireEvent("panelactivated", this, panel);
36118     },
36119
36120     /**
36121      * Shows the specified panel.
36122      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36123      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36124      */
36125     showPanel : function(panel)
36126     {
36127         panel = this.getPanel(panel);
36128         if(panel){
36129             if(this.tabs){
36130                 var tab = this.tabs.getTab(panel.getEl().id);
36131                 if(tab.isHidden()){
36132                     this.tabs.unhideTab(tab.id);
36133                 }
36134                 tab.activate();
36135             }else{
36136                 this.setActivePanel(panel);
36137             }
36138         }
36139         return panel;
36140     },
36141
36142     /**
36143      * Get the active panel for this region.
36144      * @return {Roo.ContentPanel} The active panel or null
36145      */
36146     getActivePanel : function(){
36147         return this.activePanel;
36148     },
36149
36150     validateVisibility : function(){
36151         if(this.panels.getCount() < 1){
36152             this.updateTitle("&#160;");
36153             this.closeBtn.hide();
36154             this.hide();
36155         }else{
36156             if(!this.isVisible()){
36157                 this.show();
36158             }
36159         }
36160     },
36161
36162     /**
36163      * Adds the passed ContentPanel(s) to this region.
36164      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36165      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36166      */
36167     add : function(panel)
36168     {
36169         if(arguments.length > 1){
36170             for(var i = 0, len = arguments.length; i < len; i++) {
36171                 this.add(arguments[i]);
36172             }
36173             return null;
36174         }
36175         
36176         // if we have not been rendered yet, then we can not really do much of this..
36177         if (!this.bodyEl) {
36178             this.unrendered_panels.push(panel);
36179             return panel;
36180         }
36181         
36182         
36183         
36184         
36185         if(this.hasPanel(panel)){
36186             this.showPanel(panel);
36187             return panel;
36188         }
36189         panel.setRegion(this);
36190         this.panels.add(panel);
36191        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36192             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36193             // and hide them... ???
36194             this.bodyEl.dom.appendChild(panel.getEl().dom);
36195             if(panel.background !== true){
36196                 this.setActivePanel(panel);
36197             }
36198             this.fireEvent("paneladded", this, panel);
36199             return panel;
36200         }
36201         */
36202         if(!this.tabs){
36203             this.initTabs();
36204         }else{
36205             this.initPanelAsTab(panel);
36206         }
36207         
36208         
36209         if(panel.background !== true){
36210             this.tabs.activate(panel.getEl().id);
36211         }
36212         this.fireEvent("paneladded", this, panel);
36213         return panel;
36214     },
36215
36216     /**
36217      * Hides the tab for the specified panel.
36218      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36219      */
36220     hidePanel : function(panel){
36221         if(this.tabs && (panel = this.getPanel(panel))){
36222             this.tabs.hideTab(panel.getEl().id);
36223         }
36224     },
36225
36226     /**
36227      * Unhides the tab for a previously hidden panel.
36228      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36229      */
36230     unhidePanel : function(panel){
36231         if(this.tabs && (panel = this.getPanel(panel))){
36232             this.tabs.unhideTab(panel.getEl().id);
36233         }
36234     },
36235
36236     clearPanels : function(){
36237         while(this.panels.getCount() > 0){
36238              this.remove(this.panels.first());
36239         }
36240     },
36241
36242     /**
36243      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36244      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36245      * @param {Boolean} preservePanel Overrides the config preservePanel option
36246      * @return {Roo.ContentPanel} The panel that was removed
36247      */
36248     remove : function(panel, preservePanel)
36249     {
36250         panel = this.getPanel(panel);
36251         if(!panel){
36252             return null;
36253         }
36254         var e = {};
36255         this.fireEvent("beforeremove", this, panel, e);
36256         if(e.cancel === true){
36257             return null;
36258         }
36259         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36260         var panelId = panel.getId();
36261         this.panels.removeKey(panelId);
36262         if(preservePanel){
36263             document.body.appendChild(panel.getEl().dom);
36264         }
36265         if(this.tabs){
36266             this.tabs.removeTab(panel.getEl().id);
36267         }else if (!preservePanel){
36268             this.bodyEl.dom.removeChild(panel.getEl().dom);
36269         }
36270         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36271             var p = this.panels.first();
36272             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36273             tempEl.appendChild(p.getEl().dom);
36274             this.bodyEl.update("");
36275             this.bodyEl.dom.appendChild(p.getEl().dom);
36276             tempEl = null;
36277             this.updateTitle(p.getTitle());
36278             this.tabs = null;
36279             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36280             this.setActivePanel(p);
36281         }
36282         panel.setRegion(null);
36283         if(this.activePanel == panel){
36284             this.activePanel = null;
36285         }
36286         if(this.config.autoDestroy !== false && preservePanel !== true){
36287             try{panel.destroy();}catch(e){}
36288         }
36289         this.fireEvent("panelremoved", this, panel);
36290         return panel;
36291     },
36292
36293     /**
36294      * Returns the TabPanel component used by this region
36295      * @return {Roo.TabPanel}
36296      */
36297     getTabs : function(){
36298         return this.tabs;
36299     },
36300
36301     createTool : function(parentEl, className){
36302         var btn = Roo.DomHelper.append(parentEl, {
36303             tag: "div",
36304             cls: "x-layout-tools-button",
36305             children: [ {
36306                 tag: "div",
36307                 cls: "roo-layout-tools-button-inner " + className,
36308                 html: "&#160;"
36309             }]
36310         }, true);
36311         btn.addClassOnOver("roo-layout-tools-button-over");
36312         return btn;
36313     }
36314 });/*
36315  * Based on:
36316  * Ext JS Library 1.1.1
36317  * Copyright(c) 2006-2007, Ext JS, LLC.
36318  *
36319  * Originally Released Under LGPL - original licence link has changed is not relivant.
36320  *
36321  * Fork - LGPL
36322  * <script type="text/javascript">
36323  */
36324  
36325
36326
36327 /**
36328  * @class Roo.SplitLayoutRegion
36329  * @extends Roo.LayoutRegion
36330  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36331  */
36332 Roo.bootstrap.layout.Split = function(config){
36333     this.cursor = config.cursor;
36334     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36335 };
36336
36337 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36338 {
36339     splitTip : "Drag to resize.",
36340     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36341     useSplitTips : false,
36342
36343     applyConfig : function(config){
36344         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36345     },
36346     
36347     onRender : function(ctr,pos) {
36348         
36349         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36350         if(!this.config.split){
36351             return;
36352         }
36353         if(!this.split){
36354             
36355             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36356                             tag: "div",
36357                             id: this.el.id + "-split",
36358                             cls: "roo-layout-split roo-layout-split-"+this.position,
36359                             html: "&#160;"
36360             });
36361             /** The SplitBar for this region 
36362             * @type Roo.SplitBar */
36363             // does not exist yet...
36364             Roo.log([this.position, this.orientation]);
36365             
36366             this.split = new Roo.bootstrap.SplitBar({
36367                 dragElement : splitEl,
36368                 resizingElement: this.el,
36369                 orientation : this.orientation
36370             });
36371             
36372             this.split.on("moved", this.onSplitMove, this);
36373             this.split.useShim = this.config.useShim === true;
36374             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36375             if(this.useSplitTips){
36376                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36377             }
36378             //if(config.collapsible){
36379             //    this.split.el.on("dblclick", this.collapse,  this);
36380             //}
36381         }
36382         if(typeof this.config.minSize != "undefined"){
36383             this.split.minSize = this.config.minSize;
36384         }
36385         if(typeof this.config.maxSize != "undefined"){
36386             this.split.maxSize = this.config.maxSize;
36387         }
36388         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36389             this.hideSplitter();
36390         }
36391         
36392     },
36393
36394     getHMaxSize : function(){
36395          var cmax = this.config.maxSize || 10000;
36396          var center = this.mgr.getRegion("center");
36397          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36398     },
36399
36400     getVMaxSize : function(){
36401          var cmax = this.config.maxSize || 10000;
36402          var center = this.mgr.getRegion("center");
36403          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36404     },
36405
36406     onSplitMove : function(split, newSize){
36407         this.fireEvent("resized", this, newSize);
36408     },
36409     
36410     /** 
36411      * Returns the {@link Roo.SplitBar} for this region.
36412      * @return {Roo.SplitBar}
36413      */
36414     getSplitBar : function(){
36415         return this.split;
36416     },
36417     
36418     hide : function(){
36419         this.hideSplitter();
36420         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36421     },
36422
36423     hideSplitter : function(){
36424         if(this.split){
36425             this.split.el.setLocation(-2000,-2000);
36426             this.split.el.hide();
36427         }
36428     },
36429
36430     show : function(){
36431         if(this.split){
36432             this.split.el.show();
36433         }
36434         Roo.bootstrap.layout.Split.superclass.show.call(this);
36435     },
36436     
36437     beforeSlide: function(){
36438         if(Roo.isGecko){// firefox overflow auto bug workaround
36439             this.bodyEl.clip();
36440             if(this.tabs) {
36441                 this.tabs.bodyEl.clip();
36442             }
36443             if(this.activePanel){
36444                 this.activePanel.getEl().clip();
36445                 
36446                 if(this.activePanel.beforeSlide){
36447                     this.activePanel.beforeSlide();
36448                 }
36449             }
36450         }
36451     },
36452     
36453     afterSlide : function(){
36454         if(Roo.isGecko){// firefox overflow auto bug workaround
36455             this.bodyEl.unclip();
36456             if(this.tabs) {
36457                 this.tabs.bodyEl.unclip();
36458             }
36459             if(this.activePanel){
36460                 this.activePanel.getEl().unclip();
36461                 if(this.activePanel.afterSlide){
36462                     this.activePanel.afterSlide();
36463                 }
36464             }
36465         }
36466     },
36467
36468     initAutoHide : function(){
36469         if(this.autoHide !== false){
36470             if(!this.autoHideHd){
36471                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36472                 this.autoHideHd = {
36473                     "mouseout": function(e){
36474                         if(!e.within(this.el, true)){
36475                             st.delay(500);
36476                         }
36477                     },
36478                     "mouseover" : function(e){
36479                         st.cancel();
36480                     },
36481                     scope : this
36482                 };
36483             }
36484             this.el.on(this.autoHideHd);
36485         }
36486     },
36487
36488     clearAutoHide : function(){
36489         if(this.autoHide !== false){
36490             this.el.un("mouseout", this.autoHideHd.mouseout);
36491             this.el.un("mouseover", this.autoHideHd.mouseover);
36492         }
36493     },
36494
36495     clearMonitor : function(){
36496         Roo.get(document).un("click", this.slideInIf, this);
36497     },
36498
36499     // these names are backwards but not changed for compat
36500     slideOut : function(){
36501         if(this.isSlid || this.el.hasActiveFx()){
36502             return;
36503         }
36504         this.isSlid = true;
36505         if(this.collapseBtn){
36506             this.collapseBtn.hide();
36507         }
36508         this.closeBtnState = this.closeBtn.getStyle('display');
36509         this.closeBtn.hide();
36510         if(this.stickBtn){
36511             this.stickBtn.show();
36512         }
36513         this.el.show();
36514         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36515         this.beforeSlide();
36516         this.el.setStyle("z-index", 10001);
36517         this.el.slideIn(this.getSlideAnchor(), {
36518             callback: function(){
36519                 this.afterSlide();
36520                 this.initAutoHide();
36521                 Roo.get(document).on("click", this.slideInIf, this);
36522                 this.fireEvent("slideshow", this);
36523             },
36524             scope: this,
36525             block: true
36526         });
36527     },
36528
36529     afterSlideIn : function(){
36530         this.clearAutoHide();
36531         this.isSlid = false;
36532         this.clearMonitor();
36533         this.el.setStyle("z-index", "");
36534         if(this.collapseBtn){
36535             this.collapseBtn.show();
36536         }
36537         this.closeBtn.setStyle('display', this.closeBtnState);
36538         if(this.stickBtn){
36539             this.stickBtn.hide();
36540         }
36541         this.fireEvent("slidehide", this);
36542     },
36543
36544     slideIn : function(cb){
36545         if(!this.isSlid || this.el.hasActiveFx()){
36546             Roo.callback(cb);
36547             return;
36548         }
36549         this.isSlid = false;
36550         this.beforeSlide();
36551         this.el.slideOut(this.getSlideAnchor(), {
36552             callback: function(){
36553                 this.el.setLeftTop(-10000, -10000);
36554                 this.afterSlide();
36555                 this.afterSlideIn();
36556                 Roo.callback(cb);
36557             },
36558             scope: this,
36559             block: true
36560         });
36561     },
36562     
36563     slideInIf : function(e){
36564         if(!e.within(this.el)){
36565             this.slideIn();
36566         }
36567     },
36568
36569     animateCollapse : function(){
36570         this.beforeSlide();
36571         this.el.setStyle("z-index", 20000);
36572         var anchor = this.getSlideAnchor();
36573         this.el.slideOut(anchor, {
36574             callback : function(){
36575                 this.el.setStyle("z-index", "");
36576                 this.collapsedEl.slideIn(anchor, {duration:.3});
36577                 this.afterSlide();
36578                 this.el.setLocation(-10000,-10000);
36579                 this.el.hide();
36580                 this.fireEvent("collapsed", this);
36581             },
36582             scope: this,
36583             block: true
36584         });
36585     },
36586
36587     animateExpand : function(){
36588         this.beforeSlide();
36589         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36590         this.el.setStyle("z-index", 20000);
36591         this.collapsedEl.hide({
36592             duration:.1
36593         });
36594         this.el.slideIn(this.getSlideAnchor(), {
36595             callback : function(){
36596                 this.el.setStyle("z-index", "");
36597                 this.afterSlide();
36598                 if(this.split){
36599                     this.split.el.show();
36600                 }
36601                 this.fireEvent("invalidated", this);
36602                 this.fireEvent("expanded", this);
36603             },
36604             scope: this,
36605             block: true
36606         });
36607     },
36608
36609     anchors : {
36610         "west" : "left",
36611         "east" : "right",
36612         "north" : "top",
36613         "south" : "bottom"
36614     },
36615
36616     sanchors : {
36617         "west" : "l",
36618         "east" : "r",
36619         "north" : "t",
36620         "south" : "b"
36621     },
36622
36623     canchors : {
36624         "west" : "tl-tr",
36625         "east" : "tr-tl",
36626         "north" : "tl-bl",
36627         "south" : "bl-tl"
36628     },
36629
36630     getAnchor : function(){
36631         return this.anchors[this.position];
36632     },
36633
36634     getCollapseAnchor : function(){
36635         return this.canchors[this.position];
36636     },
36637
36638     getSlideAnchor : function(){
36639         return this.sanchors[this.position];
36640     },
36641
36642     getAlignAdj : function(){
36643         var cm = this.cmargins;
36644         switch(this.position){
36645             case "west":
36646                 return [0, 0];
36647             break;
36648             case "east":
36649                 return [0, 0];
36650             break;
36651             case "north":
36652                 return [0, 0];
36653             break;
36654             case "south":
36655                 return [0, 0];
36656             break;
36657         }
36658     },
36659
36660     getExpandAdj : function(){
36661         var c = this.collapsedEl, cm = this.cmargins;
36662         switch(this.position){
36663             case "west":
36664                 return [-(cm.right+c.getWidth()+cm.left), 0];
36665             break;
36666             case "east":
36667                 return [cm.right+c.getWidth()+cm.left, 0];
36668             break;
36669             case "north":
36670                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36671             break;
36672             case "south":
36673                 return [0, cm.top+cm.bottom+c.getHeight()];
36674             break;
36675         }
36676     }
36677 });/*
36678  * Based on:
36679  * Ext JS Library 1.1.1
36680  * Copyright(c) 2006-2007, Ext JS, LLC.
36681  *
36682  * Originally Released Under LGPL - original licence link has changed is not relivant.
36683  *
36684  * Fork - LGPL
36685  * <script type="text/javascript">
36686  */
36687 /*
36688  * These classes are private internal classes
36689  */
36690 Roo.bootstrap.layout.Center = function(config){
36691     config.region = "center";
36692     Roo.bootstrap.layout.Region.call(this, config);
36693     this.visible = true;
36694     this.minWidth = config.minWidth || 20;
36695     this.minHeight = config.minHeight || 20;
36696 };
36697
36698 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36699     hide : function(){
36700         // center panel can't be hidden
36701     },
36702     
36703     show : function(){
36704         // center panel can't be hidden
36705     },
36706     
36707     getMinWidth: function(){
36708         return this.minWidth;
36709     },
36710     
36711     getMinHeight: function(){
36712         return this.minHeight;
36713     }
36714 });
36715
36716
36717
36718
36719  
36720
36721
36722
36723
36724
36725 Roo.bootstrap.layout.North = function(config)
36726 {
36727     config.region = 'north';
36728     config.cursor = 'n-resize';
36729     
36730     Roo.bootstrap.layout.Split.call(this, config);
36731     
36732     
36733     if(this.split){
36734         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36735         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36736         this.split.el.addClass("roo-layout-split-v");
36737     }
36738     var size = config.initialSize || config.height;
36739     if(typeof size != "undefined"){
36740         this.el.setHeight(size);
36741     }
36742 };
36743 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36744 {
36745     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36746     
36747     
36748     
36749     getBox : function(){
36750         if(this.collapsed){
36751             return this.collapsedEl.getBox();
36752         }
36753         var box = this.el.getBox();
36754         if(this.split){
36755             box.height += this.split.el.getHeight();
36756         }
36757         return box;
36758     },
36759     
36760     updateBox : function(box){
36761         if(this.split && !this.collapsed){
36762             box.height -= this.split.el.getHeight();
36763             this.split.el.setLeft(box.x);
36764             this.split.el.setTop(box.y+box.height);
36765             this.split.el.setWidth(box.width);
36766         }
36767         if(this.collapsed){
36768             this.updateBody(box.width, null);
36769         }
36770         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36771     }
36772 });
36773
36774
36775
36776
36777
36778 Roo.bootstrap.layout.South = function(config){
36779     config.region = 'south';
36780     config.cursor = 's-resize';
36781     Roo.bootstrap.layout.Split.call(this, config);
36782     if(this.split){
36783         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36784         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36785         this.split.el.addClass("roo-layout-split-v");
36786     }
36787     var size = config.initialSize || config.height;
36788     if(typeof size != "undefined"){
36789         this.el.setHeight(size);
36790     }
36791 };
36792
36793 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36794     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36795     getBox : function(){
36796         if(this.collapsed){
36797             return this.collapsedEl.getBox();
36798         }
36799         var box = this.el.getBox();
36800         if(this.split){
36801             var sh = this.split.el.getHeight();
36802             box.height += sh;
36803             box.y -= sh;
36804         }
36805         return box;
36806     },
36807     
36808     updateBox : function(box){
36809         if(this.split && !this.collapsed){
36810             var sh = this.split.el.getHeight();
36811             box.height -= sh;
36812             box.y += sh;
36813             this.split.el.setLeft(box.x);
36814             this.split.el.setTop(box.y-sh);
36815             this.split.el.setWidth(box.width);
36816         }
36817         if(this.collapsed){
36818             this.updateBody(box.width, null);
36819         }
36820         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36821     }
36822 });
36823
36824 Roo.bootstrap.layout.East = function(config){
36825     config.region = "east";
36826     config.cursor = "e-resize";
36827     Roo.bootstrap.layout.Split.call(this, config);
36828     if(this.split){
36829         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36830         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36831         this.split.el.addClass("roo-layout-split-h");
36832     }
36833     var size = config.initialSize || config.width;
36834     if(typeof size != "undefined"){
36835         this.el.setWidth(size);
36836     }
36837 };
36838 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36839     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36840     getBox : function(){
36841         if(this.collapsed){
36842             return this.collapsedEl.getBox();
36843         }
36844         var box = this.el.getBox();
36845         if(this.split){
36846             var sw = this.split.el.getWidth();
36847             box.width += sw;
36848             box.x -= sw;
36849         }
36850         return box;
36851     },
36852
36853     updateBox : function(box){
36854         if(this.split && !this.collapsed){
36855             var sw = this.split.el.getWidth();
36856             box.width -= sw;
36857             this.split.el.setLeft(box.x);
36858             this.split.el.setTop(box.y);
36859             this.split.el.setHeight(box.height);
36860             box.x += sw;
36861         }
36862         if(this.collapsed){
36863             this.updateBody(null, box.height);
36864         }
36865         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36866     }
36867 });
36868
36869 Roo.bootstrap.layout.West = function(config){
36870     config.region = "west";
36871     config.cursor = "w-resize";
36872     
36873     Roo.bootstrap.layout.Split.call(this, config);
36874     if(this.split){
36875         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36876         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36877         this.split.el.addClass("roo-layout-split-h");
36878     }
36879     
36880 };
36881 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36882     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36883     
36884     onRender: function(ctr, pos)
36885     {
36886         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36887         var size = this.config.initialSize || this.config.width;
36888         if(typeof size != "undefined"){
36889             this.el.setWidth(size);
36890         }
36891     },
36892     
36893     getBox : function(){
36894         if(this.collapsed){
36895             return this.collapsedEl.getBox();
36896         }
36897         var box = this.el.getBox();
36898         if(this.split){
36899             box.width += this.split.el.getWidth();
36900         }
36901         return box;
36902     },
36903     
36904     updateBox : function(box){
36905         if(this.split && !this.collapsed){
36906             var sw = this.split.el.getWidth();
36907             box.width -= sw;
36908             this.split.el.setLeft(box.x+box.width);
36909             this.split.el.setTop(box.y);
36910             this.split.el.setHeight(box.height);
36911         }
36912         if(this.collapsed){
36913             this.updateBody(null, box.height);
36914         }
36915         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36916     }
36917 });
36918 Roo.namespace("Roo.bootstrap.panel");/*
36919  * Based on:
36920  * Ext JS Library 1.1.1
36921  * Copyright(c) 2006-2007, Ext JS, LLC.
36922  *
36923  * Originally Released Under LGPL - original licence link has changed is not relivant.
36924  *
36925  * Fork - LGPL
36926  * <script type="text/javascript">
36927  */
36928 /**
36929  * @class Roo.ContentPanel
36930  * @extends Roo.util.Observable
36931  * A basic ContentPanel element.
36932  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36933  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36934  * @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
36935  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36936  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36937  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36938  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36939  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36940  * @cfg {String} title          The title for this panel
36941  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36942  * @cfg {String} url            Calls {@link #setUrl} with this value
36943  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36944  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36945  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36946  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36947  * @cfg {Boolean} badges render the badges
36948
36949  * @constructor
36950  * Create a new ContentPanel.
36951  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36952  * @param {String/Object} config A string to set only the title or a config object
36953  * @param {String} content (optional) Set the HTML content for this panel
36954  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36955  */
36956 Roo.bootstrap.panel.Content = function( config){
36957     
36958     this.tpl = config.tpl || false;
36959     
36960     var el = config.el;
36961     var content = config.content;
36962
36963     if(config.autoCreate){ // xtype is available if this is called from factory
36964         el = Roo.id();
36965     }
36966     this.el = Roo.get(el);
36967     if(!this.el && config && config.autoCreate){
36968         if(typeof config.autoCreate == "object"){
36969             if(!config.autoCreate.id){
36970                 config.autoCreate.id = config.id||el;
36971             }
36972             this.el = Roo.DomHelper.append(document.body,
36973                         config.autoCreate, true);
36974         }else{
36975             var elcfg =  {   tag: "div",
36976                             cls: "roo-layout-inactive-content",
36977                             id: config.id||el
36978                             };
36979             if (config.html) {
36980                 elcfg.html = config.html;
36981                 
36982             }
36983                         
36984             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36985         }
36986     } 
36987     this.closable = false;
36988     this.loaded = false;
36989     this.active = false;
36990    
36991       
36992     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36993         
36994         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36995         
36996         this.wrapEl = this.el; //this.el.wrap();
36997         var ti = [];
36998         if (config.toolbar.items) {
36999             ti = config.toolbar.items ;
37000             delete config.toolbar.items ;
37001         }
37002         
37003         var nitems = [];
37004         this.toolbar.render(this.wrapEl, 'before');
37005         for(var i =0;i < ti.length;i++) {
37006           //  Roo.log(['add child', items[i]]);
37007             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37008         }
37009         this.toolbar.items = nitems;
37010         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37011         delete config.toolbar;
37012         
37013     }
37014     /*
37015     // xtype created footer. - not sure if will work as we normally have to render first..
37016     if (this.footer && !this.footer.el && this.footer.xtype) {
37017         if (!this.wrapEl) {
37018             this.wrapEl = this.el.wrap();
37019         }
37020     
37021         this.footer.container = this.wrapEl.createChild();
37022          
37023         this.footer = Roo.factory(this.footer, Roo);
37024         
37025     }
37026     */
37027     
37028      if(typeof config == "string"){
37029         this.title = config;
37030     }else{
37031         Roo.apply(this, config);
37032     }
37033     
37034     if(this.resizeEl){
37035         this.resizeEl = Roo.get(this.resizeEl, true);
37036     }else{
37037         this.resizeEl = this.el;
37038     }
37039     // handle view.xtype
37040     
37041  
37042     
37043     
37044     this.addEvents({
37045         /**
37046          * @event activate
37047          * Fires when this panel is activated. 
37048          * @param {Roo.ContentPanel} this
37049          */
37050         "activate" : true,
37051         /**
37052          * @event deactivate
37053          * Fires when this panel is activated. 
37054          * @param {Roo.ContentPanel} this
37055          */
37056         "deactivate" : true,
37057
37058         /**
37059          * @event resize
37060          * Fires when this panel is resized if fitToFrame is true.
37061          * @param {Roo.ContentPanel} this
37062          * @param {Number} width The width after any component adjustments
37063          * @param {Number} height The height after any component adjustments
37064          */
37065         "resize" : true,
37066         
37067          /**
37068          * @event render
37069          * Fires when this tab is created
37070          * @param {Roo.ContentPanel} this
37071          */
37072         "render" : true
37073         
37074         
37075         
37076     });
37077     
37078
37079     
37080     
37081     if(this.autoScroll){
37082         this.resizeEl.setStyle("overflow", "auto");
37083     } else {
37084         // fix randome scrolling
37085         //this.el.on('scroll', function() {
37086         //    Roo.log('fix random scolling');
37087         //    this.scrollTo('top',0); 
37088         //});
37089     }
37090     content = content || this.content;
37091     if(content){
37092         this.setContent(content);
37093     }
37094     if(config && config.url){
37095         this.setUrl(this.url, this.params, this.loadOnce);
37096     }
37097     
37098     
37099     
37100     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37101     
37102     if (this.view && typeof(this.view.xtype) != 'undefined') {
37103         this.view.el = this.el.appendChild(document.createElement("div"));
37104         this.view = Roo.factory(this.view); 
37105         this.view.render  &&  this.view.render(false, '');  
37106     }
37107     
37108     
37109     this.fireEvent('render', this);
37110 };
37111
37112 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37113     
37114     tabTip : '',
37115     
37116     setRegion : function(region){
37117         this.region = region;
37118         this.setActiveClass(region && !this.background);
37119     },
37120     
37121     
37122     setActiveClass: function(state)
37123     {
37124         if(state){
37125            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37126            this.el.setStyle('position','relative');
37127         }else{
37128            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37129            this.el.setStyle('position', 'absolute');
37130         } 
37131     },
37132     
37133     /**
37134      * Returns the toolbar for this Panel if one was configured. 
37135      * @return {Roo.Toolbar} 
37136      */
37137     getToolbar : function(){
37138         return this.toolbar;
37139     },
37140     
37141     setActiveState : function(active)
37142     {
37143         this.active = active;
37144         this.setActiveClass(active);
37145         if(!active){
37146             if(this.fireEvent("deactivate", this) === false){
37147                 return false;
37148             }
37149             return true;
37150         }
37151         this.fireEvent("activate", this);
37152         return true;
37153     },
37154     /**
37155      * Updates this panel's element
37156      * @param {String} content The new content
37157      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37158     */
37159     setContent : function(content, loadScripts){
37160         this.el.update(content, loadScripts);
37161     },
37162
37163     ignoreResize : function(w, h){
37164         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37165             return true;
37166         }else{
37167             this.lastSize = {width: w, height: h};
37168             return false;
37169         }
37170     },
37171     /**
37172      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37173      * @return {Roo.UpdateManager} The UpdateManager
37174      */
37175     getUpdateManager : function(){
37176         return this.el.getUpdateManager();
37177     },
37178      /**
37179      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37180      * @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:
37181 <pre><code>
37182 panel.load({
37183     url: "your-url.php",
37184     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37185     callback: yourFunction,
37186     scope: yourObject, //(optional scope)
37187     discardUrl: false,
37188     nocache: false,
37189     text: "Loading...",
37190     timeout: 30,
37191     scripts: false
37192 });
37193 </code></pre>
37194      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37195      * 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.
37196      * @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}
37197      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37198      * @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.
37199      * @return {Roo.ContentPanel} this
37200      */
37201     load : function(){
37202         var um = this.el.getUpdateManager();
37203         um.update.apply(um, arguments);
37204         return this;
37205     },
37206
37207
37208     /**
37209      * 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.
37210      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37211      * @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)
37212      * @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)
37213      * @return {Roo.UpdateManager} The UpdateManager
37214      */
37215     setUrl : function(url, params, loadOnce){
37216         if(this.refreshDelegate){
37217             this.removeListener("activate", this.refreshDelegate);
37218         }
37219         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37220         this.on("activate", this.refreshDelegate);
37221         return this.el.getUpdateManager();
37222     },
37223     
37224     _handleRefresh : function(url, params, loadOnce){
37225         if(!loadOnce || !this.loaded){
37226             var updater = this.el.getUpdateManager();
37227             updater.update(url, params, this._setLoaded.createDelegate(this));
37228         }
37229     },
37230     
37231     _setLoaded : function(){
37232         this.loaded = true;
37233     }, 
37234     
37235     /**
37236      * Returns this panel's id
37237      * @return {String} 
37238      */
37239     getId : function(){
37240         return this.el.id;
37241     },
37242     
37243     /** 
37244      * Returns this panel's element - used by regiosn to add.
37245      * @return {Roo.Element} 
37246      */
37247     getEl : function(){
37248         return this.wrapEl || this.el;
37249     },
37250     
37251    
37252     
37253     adjustForComponents : function(width, height)
37254     {
37255         //Roo.log('adjustForComponents ');
37256         if(this.resizeEl != this.el){
37257             width -= this.el.getFrameWidth('lr');
37258             height -= this.el.getFrameWidth('tb');
37259         }
37260         if(this.toolbar){
37261             var te = this.toolbar.getEl();
37262             te.setWidth(width);
37263             height -= te.getHeight();
37264         }
37265         if(this.footer){
37266             var te = this.footer.getEl();
37267             te.setWidth(width);
37268             height -= te.getHeight();
37269         }
37270         
37271         
37272         if(this.adjustments){
37273             width += this.adjustments[0];
37274             height += this.adjustments[1];
37275         }
37276         return {"width": width, "height": height};
37277     },
37278     
37279     setSize : function(width, height){
37280         if(this.fitToFrame && !this.ignoreResize(width, height)){
37281             if(this.fitContainer && this.resizeEl != this.el){
37282                 this.el.setSize(width, height);
37283             }
37284             var size = this.adjustForComponents(width, height);
37285             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37286             this.fireEvent('resize', this, size.width, size.height);
37287         }
37288     },
37289     
37290     /**
37291      * Returns this panel's title
37292      * @return {String} 
37293      */
37294     getTitle : function(){
37295         
37296         if (typeof(this.title) != 'object') {
37297             return this.title;
37298         }
37299         
37300         var t = '';
37301         for (var k in this.title) {
37302             if (!this.title.hasOwnProperty(k)) {
37303                 continue;
37304             }
37305             
37306             if (k.indexOf('-') >= 0) {
37307                 var s = k.split('-');
37308                 for (var i = 0; i<s.length; i++) {
37309                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37310                 }
37311             } else {
37312                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37313             }
37314         }
37315         return t;
37316     },
37317     
37318     /**
37319      * Set this panel's title
37320      * @param {String} title
37321      */
37322     setTitle : function(title){
37323         this.title = title;
37324         if(this.region){
37325             this.region.updatePanelTitle(this, title);
37326         }
37327     },
37328     
37329     /**
37330      * Returns true is this panel was configured to be closable
37331      * @return {Boolean} 
37332      */
37333     isClosable : function(){
37334         return this.closable;
37335     },
37336     
37337     beforeSlide : function(){
37338         this.el.clip();
37339         this.resizeEl.clip();
37340     },
37341     
37342     afterSlide : function(){
37343         this.el.unclip();
37344         this.resizeEl.unclip();
37345     },
37346     
37347     /**
37348      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37349      *   Will fail silently if the {@link #setUrl} method has not been called.
37350      *   This does not activate the panel, just updates its content.
37351      */
37352     refresh : function(){
37353         if(this.refreshDelegate){
37354            this.loaded = false;
37355            this.refreshDelegate();
37356         }
37357     },
37358     
37359     /**
37360      * Destroys this panel
37361      */
37362     destroy : function(){
37363         this.el.removeAllListeners();
37364         var tempEl = document.createElement("span");
37365         tempEl.appendChild(this.el.dom);
37366         tempEl.innerHTML = "";
37367         this.el.remove();
37368         this.el = null;
37369     },
37370     
37371     /**
37372      * form - if the content panel contains a form - this is a reference to it.
37373      * @type {Roo.form.Form}
37374      */
37375     form : false,
37376     /**
37377      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37378      *    This contains a reference to it.
37379      * @type {Roo.View}
37380      */
37381     view : false,
37382     
37383       /**
37384      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37385      * <pre><code>
37386
37387 layout.addxtype({
37388        xtype : 'Form',
37389        items: [ .... ]
37390    }
37391 );
37392
37393 </code></pre>
37394      * @param {Object} cfg Xtype definition of item to add.
37395      */
37396     
37397     
37398     getChildContainer: function () {
37399         return this.getEl();
37400     }
37401     
37402     
37403     /*
37404         var  ret = new Roo.factory(cfg);
37405         return ret;
37406         
37407         
37408         // add form..
37409         if (cfg.xtype.match(/^Form$/)) {
37410             
37411             var el;
37412             //if (this.footer) {
37413             //    el = this.footer.container.insertSibling(false, 'before');
37414             //} else {
37415                 el = this.el.createChild();
37416             //}
37417
37418             this.form = new  Roo.form.Form(cfg);
37419             
37420             
37421             if ( this.form.allItems.length) {
37422                 this.form.render(el.dom);
37423             }
37424             return this.form;
37425         }
37426         // should only have one of theses..
37427         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37428             // views.. should not be just added - used named prop 'view''
37429             
37430             cfg.el = this.el.appendChild(document.createElement("div"));
37431             // factory?
37432             
37433             var ret = new Roo.factory(cfg);
37434              
37435              ret.render && ret.render(false, ''); // render blank..
37436             this.view = ret;
37437             return ret;
37438         }
37439         return false;
37440     }
37441     \*/
37442 });
37443  
37444 /**
37445  * @class Roo.bootstrap.panel.Grid
37446  * @extends Roo.bootstrap.panel.Content
37447  * @constructor
37448  * Create a new GridPanel.
37449  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37450  * @param {Object} config A the config object
37451   
37452  */
37453
37454
37455
37456 Roo.bootstrap.panel.Grid = function(config)
37457 {
37458     
37459       
37460     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37461         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37462
37463     config.el = this.wrapper;
37464     //this.el = this.wrapper;
37465     
37466       if (config.container) {
37467         // ctor'ed from a Border/panel.grid
37468         
37469         
37470         this.wrapper.setStyle("overflow", "hidden");
37471         this.wrapper.addClass('roo-grid-container');
37472
37473     }
37474     
37475     
37476     if(config.toolbar){
37477         var tool_el = this.wrapper.createChild();    
37478         this.toolbar = Roo.factory(config.toolbar);
37479         var ti = [];
37480         if (config.toolbar.items) {
37481             ti = config.toolbar.items ;
37482             delete config.toolbar.items ;
37483         }
37484         
37485         var nitems = [];
37486         this.toolbar.render(tool_el);
37487         for(var i =0;i < ti.length;i++) {
37488           //  Roo.log(['add child', items[i]]);
37489             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37490         }
37491         this.toolbar.items = nitems;
37492         
37493         delete config.toolbar;
37494     }
37495     
37496     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37497     config.grid.scrollBody = true;;
37498     config.grid.monitorWindowResize = false; // turn off autosizing
37499     config.grid.autoHeight = false;
37500     config.grid.autoWidth = false;
37501     
37502     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37503     
37504     if (config.background) {
37505         // render grid on panel activation (if panel background)
37506         this.on('activate', function(gp) {
37507             if (!gp.grid.rendered) {
37508                 gp.grid.render(this.wrapper);
37509                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37510             }
37511         });
37512             
37513     } else {
37514         this.grid.render(this.wrapper);
37515         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37516
37517     }
37518     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37519     // ??? needed ??? config.el = this.wrapper;
37520     
37521     
37522     
37523   
37524     // xtype created footer. - not sure if will work as we normally have to render first..
37525     if (this.footer && !this.footer.el && this.footer.xtype) {
37526         
37527         var ctr = this.grid.getView().getFooterPanel(true);
37528         this.footer.dataSource = this.grid.dataSource;
37529         this.footer = Roo.factory(this.footer, Roo);
37530         this.footer.render(ctr);
37531         
37532     }
37533     
37534     
37535     
37536     
37537      
37538 };
37539
37540 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37541     getId : function(){
37542         return this.grid.id;
37543     },
37544     
37545     /**
37546      * Returns the grid for this panel
37547      * @return {Roo.bootstrap.Table} 
37548      */
37549     getGrid : function(){
37550         return this.grid;    
37551     },
37552     
37553     setSize : function(width, height){
37554         if(!this.ignoreResize(width, height)){
37555             var grid = this.grid;
37556             var size = this.adjustForComponents(width, height);
37557             var gridel = grid.getGridEl();
37558             gridel.setSize(size.width, size.height);
37559             /*
37560             var thd = grid.getGridEl().select('thead',true).first();
37561             var tbd = grid.getGridEl().select('tbody', true).first();
37562             if (tbd) {
37563                 tbd.setSize(width, height - thd.getHeight());
37564             }
37565             */
37566             grid.autoSize();
37567         }
37568     },
37569      
37570     
37571     
37572     beforeSlide : function(){
37573         this.grid.getView().scroller.clip();
37574     },
37575     
37576     afterSlide : function(){
37577         this.grid.getView().scroller.unclip();
37578     },
37579     
37580     destroy : function(){
37581         this.grid.destroy();
37582         delete this.grid;
37583         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37584     }
37585 });
37586
37587 /**
37588  * @class Roo.bootstrap.panel.Nest
37589  * @extends Roo.bootstrap.panel.Content
37590  * @constructor
37591  * Create a new Panel, that can contain a layout.Border.
37592  * 
37593  * 
37594  * @param {Roo.BorderLayout} layout The layout for this panel
37595  * @param {String/Object} config A string to set only the title or a config object
37596  */
37597 Roo.bootstrap.panel.Nest = function(config)
37598 {
37599     // construct with only one argument..
37600     /* FIXME - implement nicer consturctors
37601     if (layout.layout) {
37602         config = layout;
37603         layout = config.layout;
37604         delete config.layout;
37605     }
37606     if (layout.xtype && !layout.getEl) {
37607         // then layout needs constructing..
37608         layout = Roo.factory(layout, Roo);
37609     }
37610     */
37611     
37612     config.el =  config.layout.getEl();
37613     
37614     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37615     
37616     config.layout.monitorWindowResize = false; // turn off autosizing
37617     this.layout = config.layout;
37618     this.layout.getEl().addClass("roo-layout-nested-layout");
37619     
37620     
37621     
37622     
37623 };
37624
37625 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37626
37627     setSize : function(width, height){
37628         if(!this.ignoreResize(width, height)){
37629             var size = this.adjustForComponents(width, height);
37630             var el = this.layout.getEl();
37631             if (size.height < 1) {
37632                 el.setWidth(size.width);   
37633             } else {
37634                 el.setSize(size.width, size.height);
37635             }
37636             var touch = el.dom.offsetWidth;
37637             this.layout.layout();
37638             // ie requires a double layout on the first pass
37639             if(Roo.isIE && !this.initialized){
37640                 this.initialized = true;
37641                 this.layout.layout();
37642             }
37643         }
37644     },
37645     
37646     // activate all subpanels if not currently active..
37647     
37648     setActiveState : function(active){
37649         this.active = active;
37650         this.setActiveClass(active);
37651         
37652         if(!active){
37653             this.fireEvent("deactivate", this);
37654             return;
37655         }
37656         
37657         this.fireEvent("activate", this);
37658         // not sure if this should happen before or after..
37659         if (!this.layout) {
37660             return; // should not happen..
37661         }
37662         var reg = false;
37663         for (var r in this.layout.regions) {
37664             reg = this.layout.getRegion(r);
37665             if (reg.getActivePanel()) {
37666                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37667                 reg.setActivePanel(reg.getActivePanel());
37668                 continue;
37669             }
37670             if (!reg.panels.length) {
37671                 continue;
37672             }
37673             reg.showPanel(reg.getPanel(0));
37674         }
37675         
37676         
37677         
37678         
37679     },
37680     
37681     /**
37682      * Returns the nested BorderLayout for this panel
37683      * @return {Roo.BorderLayout} 
37684      */
37685     getLayout : function(){
37686         return this.layout;
37687     },
37688     
37689      /**
37690      * Adds a xtype elements to the layout of the nested panel
37691      * <pre><code>
37692
37693 panel.addxtype({
37694        xtype : 'ContentPanel',
37695        region: 'west',
37696        items: [ .... ]
37697    }
37698 );
37699
37700 panel.addxtype({
37701         xtype : 'NestedLayoutPanel',
37702         region: 'west',
37703         layout: {
37704            center: { },
37705            west: { }   
37706         },
37707         items : [ ... list of content panels or nested layout panels.. ]
37708    }
37709 );
37710 </code></pre>
37711      * @param {Object} cfg Xtype definition of item to add.
37712      */
37713     addxtype : function(cfg) {
37714         return this.layout.addxtype(cfg);
37715     
37716     }
37717 });        /*
37718  * Based on:
37719  * Ext JS Library 1.1.1
37720  * Copyright(c) 2006-2007, Ext JS, LLC.
37721  *
37722  * Originally Released Under LGPL - original licence link has changed is not relivant.
37723  *
37724  * Fork - LGPL
37725  * <script type="text/javascript">
37726  */
37727 /**
37728  * @class Roo.TabPanel
37729  * @extends Roo.util.Observable
37730  * A lightweight tab container.
37731  * <br><br>
37732  * Usage:
37733  * <pre><code>
37734 // basic tabs 1, built from existing content
37735 var tabs = new Roo.TabPanel("tabs1");
37736 tabs.addTab("script", "View Script");
37737 tabs.addTab("markup", "View Markup");
37738 tabs.activate("script");
37739
37740 // more advanced tabs, built from javascript
37741 var jtabs = new Roo.TabPanel("jtabs");
37742 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37743
37744 // set up the UpdateManager
37745 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37746 var updater = tab2.getUpdateManager();
37747 updater.setDefaultUrl("ajax1.htm");
37748 tab2.on('activate', updater.refresh, updater, true);
37749
37750 // Use setUrl for Ajax loading
37751 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37752 tab3.setUrl("ajax2.htm", null, true);
37753
37754 // Disabled tab
37755 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37756 tab4.disable();
37757
37758 jtabs.activate("jtabs-1");
37759  * </code></pre>
37760  * @constructor
37761  * Create a new TabPanel.
37762  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37763  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37764  */
37765 Roo.bootstrap.panel.Tabs = function(config){
37766     /**
37767     * The container element for this TabPanel.
37768     * @type Roo.Element
37769     */
37770     this.el = Roo.get(config.el);
37771     delete config.el;
37772     if(config){
37773         if(typeof config == "boolean"){
37774             this.tabPosition = config ? "bottom" : "top";
37775         }else{
37776             Roo.apply(this, config);
37777         }
37778     }
37779     
37780     if(this.tabPosition == "bottom"){
37781         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37782         this.el.addClass("roo-tabs-bottom");
37783     }
37784     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37785     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37786     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37787     if(Roo.isIE){
37788         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37789     }
37790     if(this.tabPosition != "bottom"){
37791         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37792          * @type Roo.Element
37793          */
37794         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37795         this.el.addClass("roo-tabs-top");
37796     }
37797     this.items = [];
37798
37799     this.bodyEl.setStyle("position", "relative");
37800
37801     this.active = null;
37802     this.activateDelegate = this.activate.createDelegate(this);
37803
37804     this.addEvents({
37805         /**
37806          * @event tabchange
37807          * Fires when the active tab changes
37808          * @param {Roo.TabPanel} this
37809          * @param {Roo.TabPanelItem} activePanel The new active tab
37810          */
37811         "tabchange": true,
37812         /**
37813          * @event beforetabchange
37814          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37815          * @param {Roo.TabPanel} this
37816          * @param {Object} e Set cancel to true on this object to cancel the tab change
37817          * @param {Roo.TabPanelItem} tab The tab being changed to
37818          */
37819         "beforetabchange" : true
37820     });
37821
37822     Roo.EventManager.onWindowResize(this.onResize, this);
37823     this.cpad = this.el.getPadding("lr");
37824     this.hiddenCount = 0;
37825
37826
37827     // toolbar on the tabbar support...
37828     if (this.toolbar) {
37829         alert("no toolbar support yet");
37830         this.toolbar  = false;
37831         /*
37832         var tcfg = this.toolbar;
37833         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37834         this.toolbar = new Roo.Toolbar(tcfg);
37835         if (Roo.isSafari) {
37836             var tbl = tcfg.container.child('table', true);
37837             tbl.setAttribute('width', '100%');
37838         }
37839         */
37840         
37841     }
37842    
37843
37844
37845     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37846 };
37847
37848 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37849     /*
37850      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37851      */
37852     tabPosition : "top",
37853     /*
37854      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37855      */
37856     currentTabWidth : 0,
37857     /*
37858      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37859      */
37860     minTabWidth : 40,
37861     /*
37862      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37863      */
37864     maxTabWidth : 250,
37865     /*
37866      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37867      */
37868     preferredTabWidth : 175,
37869     /*
37870      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37871      */
37872     resizeTabs : false,
37873     /*
37874      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37875      */
37876     monitorResize : true,
37877     /*
37878      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37879      */
37880     toolbar : false,
37881
37882     /**
37883      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37884      * @param {String} id The id of the div to use <b>or create</b>
37885      * @param {String} text The text for the tab
37886      * @param {String} content (optional) Content to put in the TabPanelItem body
37887      * @param {Boolean} closable (optional) True to create a close icon on the tab
37888      * @return {Roo.TabPanelItem} The created TabPanelItem
37889      */
37890     addTab : function(id, text, content, closable, tpl)
37891     {
37892         var item = new Roo.bootstrap.panel.TabItem({
37893             panel: this,
37894             id : id,
37895             text : text,
37896             closable : closable,
37897             tpl : tpl
37898         });
37899         this.addTabItem(item);
37900         if(content){
37901             item.setContent(content);
37902         }
37903         return item;
37904     },
37905
37906     /**
37907      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37908      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37909      * @return {Roo.TabPanelItem}
37910      */
37911     getTab : function(id){
37912         return this.items[id];
37913     },
37914
37915     /**
37916      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37917      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37918      */
37919     hideTab : function(id){
37920         var t = this.items[id];
37921         if(!t.isHidden()){
37922            t.setHidden(true);
37923            this.hiddenCount++;
37924            this.autoSizeTabs();
37925         }
37926     },
37927
37928     /**
37929      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37930      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37931      */
37932     unhideTab : function(id){
37933         var t = this.items[id];
37934         if(t.isHidden()){
37935            t.setHidden(false);
37936            this.hiddenCount--;
37937            this.autoSizeTabs();
37938         }
37939     },
37940
37941     /**
37942      * Adds an existing {@link Roo.TabPanelItem}.
37943      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37944      */
37945     addTabItem : function(item){
37946         this.items[item.id] = item;
37947         this.items.push(item);
37948       //  if(this.resizeTabs){
37949     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37950   //         this.autoSizeTabs();
37951 //        }else{
37952 //            item.autoSize();
37953        // }
37954     },
37955
37956     /**
37957      * Removes a {@link Roo.TabPanelItem}.
37958      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37959      */
37960     removeTab : function(id){
37961         var items = this.items;
37962         var tab = items[id];
37963         if(!tab) { return; }
37964         var index = items.indexOf(tab);
37965         if(this.active == tab && items.length > 1){
37966             var newTab = this.getNextAvailable(index);
37967             if(newTab) {
37968                 newTab.activate();
37969             }
37970         }
37971         this.stripEl.dom.removeChild(tab.pnode.dom);
37972         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37973             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37974         }
37975         items.splice(index, 1);
37976         delete this.items[tab.id];
37977         tab.fireEvent("close", tab);
37978         tab.purgeListeners();
37979         this.autoSizeTabs();
37980     },
37981
37982     getNextAvailable : function(start){
37983         var items = this.items;
37984         var index = start;
37985         // look for a next tab that will slide over to
37986         // replace the one being removed
37987         while(index < items.length){
37988             var item = items[++index];
37989             if(item && !item.isHidden()){
37990                 return item;
37991             }
37992         }
37993         // if one isn't found select the previous tab (on the left)
37994         index = start;
37995         while(index >= 0){
37996             var item = items[--index];
37997             if(item && !item.isHidden()){
37998                 return item;
37999             }
38000         }
38001         return null;
38002     },
38003
38004     /**
38005      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38006      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38007      */
38008     disableTab : function(id){
38009         var tab = this.items[id];
38010         if(tab && this.active != tab){
38011             tab.disable();
38012         }
38013     },
38014
38015     /**
38016      * Enables a {@link Roo.TabPanelItem} that is disabled.
38017      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38018      */
38019     enableTab : function(id){
38020         var tab = this.items[id];
38021         tab.enable();
38022     },
38023
38024     /**
38025      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38026      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38027      * @return {Roo.TabPanelItem} The TabPanelItem.
38028      */
38029     activate : function(id){
38030         var tab = this.items[id];
38031         if(!tab){
38032             return null;
38033         }
38034         if(tab == this.active || tab.disabled){
38035             return tab;
38036         }
38037         var e = {};
38038         this.fireEvent("beforetabchange", this, e, tab);
38039         if(e.cancel !== true && !tab.disabled){
38040             if(this.active){
38041                 this.active.hide();
38042             }
38043             this.active = this.items[id];
38044             this.active.show();
38045             this.fireEvent("tabchange", this, this.active);
38046         }
38047         return tab;
38048     },
38049
38050     /**
38051      * Gets the active {@link Roo.TabPanelItem}.
38052      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38053      */
38054     getActiveTab : function(){
38055         return this.active;
38056     },
38057
38058     /**
38059      * Updates the tab body element to fit the height of the container element
38060      * for overflow scrolling
38061      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38062      */
38063     syncHeight : function(targetHeight){
38064         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38065         var bm = this.bodyEl.getMargins();
38066         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38067         this.bodyEl.setHeight(newHeight);
38068         return newHeight;
38069     },
38070
38071     onResize : function(){
38072         if(this.monitorResize){
38073             this.autoSizeTabs();
38074         }
38075     },
38076
38077     /**
38078      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38079      */
38080     beginUpdate : function(){
38081         this.updating = true;
38082     },
38083
38084     /**
38085      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38086      */
38087     endUpdate : function(){
38088         this.updating = false;
38089         this.autoSizeTabs();
38090     },
38091
38092     /**
38093      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38094      */
38095     autoSizeTabs : function(){
38096         var count = this.items.length;
38097         var vcount = count - this.hiddenCount;
38098         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38099             return;
38100         }
38101         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38102         var availWidth = Math.floor(w / vcount);
38103         var b = this.stripBody;
38104         if(b.getWidth() > w){
38105             var tabs = this.items;
38106             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38107             if(availWidth < this.minTabWidth){
38108                 /*if(!this.sleft){    // incomplete scrolling code
38109                     this.createScrollButtons();
38110                 }
38111                 this.showScroll();
38112                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38113             }
38114         }else{
38115             if(this.currentTabWidth < this.preferredTabWidth){
38116                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38117             }
38118         }
38119     },
38120
38121     /**
38122      * Returns the number of tabs in this TabPanel.
38123      * @return {Number}
38124      */
38125      getCount : function(){
38126          return this.items.length;
38127      },
38128
38129     /**
38130      * Resizes all the tabs to the passed width
38131      * @param {Number} The new width
38132      */
38133     setTabWidth : function(width){
38134         this.currentTabWidth = width;
38135         for(var i = 0, len = this.items.length; i < len; i++) {
38136                 if(!this.items[i].isHidden()) {
38137                 this.items[i].setWidth(width);
38138             }
38139         }
38140     },
38141
38142     /**
38143      * Destroys this TabPanel
38144      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38145      */
38146     destroy : function(removeEl){
38147         Roo.EventManager.removeResizeListener(this.onResize, this);
38148         for(var i = 0, len = this.items.length; i < len; i++){
38149             this.items[i].purgeListeners();
38150         }
38151         if(removeEl === true){
38152             this.el.update("");
38153             this.el.remove();
38154         }
38155     },
38156     
38157     createStrip : function(container)
38158     {
38159         var strip = document.createElement("nav");
38160         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38161         container.appendChild(strip);
38162         return strip;
38163     },
38164     
38165     createStripList : function(strip)
38166     {
38167         // div wrapper for retard IE
38168         // returns the "tr" element.
38169         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38170         //'<div class="x-tabs-strip-wrap">'+
38171           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38172           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38173         return strip.firstChild; //.firstChild.firstChild.firstChild;
38174     },
38175     createBody : function(container)
38176     {
38177         var body = document.createElement("div");
38178         Roo.id(body, "tab-body");
38179         //Roo.fly(body).addClass("x-tabs-body");
38180         Roo.fly(body).addClass("tab-content");
38181         container.appendChild(body);
38182         return body;
38183     },
38184     createItemBody :function(bodyEl, id){
38185         var body = Roo.getDom(id);
38186         if(!body){
38187             body = document.createElement("div");
38188             body.id = id;
38189         }
38190         //Roo.fly(body).addClass("x-tabs-item-body");
38191         Roo.fly(body).addClass("tab-pane");
38192          bodyEl.insertBefore(body, bodyEl.firstChild);
38193         return body;
38194     },
38195     /** @private */
38196     createStripElements :  function(stripEl, text, closable, tpl)
38197     {
38198         var td = document.createElement("li"); // was td..
38199         
38200         
38201         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38202         
38203         
38204         stripEl.appendChild(td);
38205         /*if(closable){
38206             td.className = "x-tabs-closable";
38207             if(!this.closeTpl){
38208                 this.closeTpl = new Roo.Template(
38209                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38210                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38211                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38212                 );
38213             }
38214             var el = this.closeTpl.overwrite(td, {"text": text});
38215             var close = el.getElementsByTagName("div")[0];
38216             var inner = el.getElementsByTagName("em")[0];
38217             return {"el": el, "close": close, "inner": inner};
38218         } else {
38219         */
38220         // not sure what this is..
38221 //            if(!this.tabTpl){
38222                 //this.tabTpl = new Roo.Template(
38223                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38224                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38225                 //);
38226 //                this.tabTpl = new Roo.Template(
38227 //                   '<a href="#">' +
38228 //                   '<span unselectable="on"' +
38229 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38230 //                            ' >{text}</span></a>'
38231 //                );
38232 //                
38233 //            }
38234
38235
38236             var template = tpl || this.tabTpl || false;
38237             
38238             if(!template){
38239                 
38240                 template = new Roo.Template(
38241                    '<a href="#">' +
38242                    '<span unselectable="on"' +
38243                             (this.disableTooltips ? '' : ' title="{text}"') +
38244                             ' >{text}</span></a>'
38245                 );
38246             }
38247             
38248             switch (typeof(template)) {
38249                 case 'object' :
38250                     break;
38251                 case 'string' :
38252                     template = new Roo.Template(template);
38253                     break;
38254                 default :
38255                     break;
38256             }
38257             
38258             var el = template.overwrite(td, {"text": text});
38259             
38260             var inner = el.getElementsByTagName("span")[0];
38261             
38262             return {"el": el, "inner": inner};
38263             
38264     }
38265         
38266     
38267 });
38268
38269 /**
38270  * @class Roo.TabPanelItem
38271  * @extends Roo.util.Observable
38272  * Represents an individual item (tab plus body) in a TabPanel.
38273  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38274  * @param {String} id The id of this TabPanelItem
38275  * @param {String} text The text for the tab of this TabPanelItem
38276  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38277  */
38278 Roo.bootstrap.panel.TabItem = function(config){
38279     /**
38280      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38281      * @type Roo.TabPanel
38282      */
38283     this.tabPanel = config.panel;
38284     /**
38285      * The id for this TabPanelItem
38286      * @type String
38287      */
38288     this.id = config.id;
38289     /** @private */
38290     this.disabled = false;
38291     /** @private */
38292     this.text = config.text;
38293     /** @private */
38294     this.loaded = false;
38295     this.closable = config.closable;
38296
38297     /**
38298      * The body element for this TabPanelItem.
38299      * @type Roo.Element
38300      */
38301     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38302     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38303     this.bodyEl.setStyle("display", "block");
38304     this.bodyEl.setStyle("zoom", "1");
38305     //this.hideAction();
38306
38307     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38308     /** @private */
38309     this.el = Roo.get(els.el);
38310     this.inner = Roo.get(els.inner, true);
38311     this.textEl = Roo.get(this.el.dom.firstChild, true);
38312     this.pnode = Roo.get(els.el.parentNode, true);
38313 //    this.el.on("mousedown", this.onTabMouseDown, this);
38314     this.el.on("click", this.onTabClick, this);
38315     /** @private */
38316     if(config.closable){
38317         var c = Roo.get(els.close, true);
38318         c.dom.title = this.closeText;
38319         c.addClassOnOver("close-over");
38320         c.on("click", this.closeClick, this);
38321      }
38322
38323     this.addEvents({
38324          /**
38325          * @event activate
38326          * Fires when this tab becomes the active tab.
38327          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38328          * @param {Roo.TabPanelItem} this
38329          */
38330         "activate": true,
38331         /**
38332          * @event beforeclose
38333          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38334          * @param {Roo.TabPanelItem} this
38335          * @param {Object} e Set cancel to true on this object to cancel the close.
38336          */
38337         "beforeclose": true,
38338         /**
38339          * @event close
38340          * Fires when this tab is closed.
38341          * @param {Roo.TabPanelItem} this
38342          */
38343          "close": true,
38344         /**
38345          * @event deactivate
38346          * Fires when this tab is no longer the active tab.
38347          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38348          * @param {Roo.TabPanelItem} this
38349          */
38350          "deactivate" : true
38351     });
38352     this.hidden = false;
38353
38354     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38355 };
38356
38357 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38358            {
38359     purgeListeners : function(){
38360        Roo.util.Observable.prototype.purgeListeners.call(this);
38361        this.el.removeAllListeners();
38362     },
38363     /**
38364      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38365      */
38366     show : function(){
38367         this.pnode.addClass("active");
38368         this.showAction();
38369         if(Roo.isOpera){
38370             this.tabPanel.stripWrap.repaint();
38371         }
38372         this.fireEvent("activate", this.tabPanel, this);
38373     },
38374
38375     /**
38376      * Returns true if this tab is the active tab.
38377      * @return {Boolean}
38378      */
38379     isActive : function(){
38380         return this.tabPanel.getActiveTab() == this;
38381     },
38382
38383     /**
38384      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38385      */
38386     hide : function(){
38387         this.pnode.removeClass("active");
38388         this.hideAction();
38389         this.fireEvent("deactivate", this.tabPanel, this);
38390     },
38391
38392     hideAction : function(){
38393         this.bodyEl.hide();
38394         this.bodyEl.setStyle("position", "absolute");
38395         this.bodyEl.setLeft("-20000px");
38396         this.bodyEl.setTop("-20000px");
38397     },
38398
38399     showAction : function(){
38400         this.bodyEl.setStyle("position", "relative");
38401         this.bodyEl.setTop("");
38402         this.bodyEl.setLeft("");
38403         this.bodyEl.show();
38404     },
38405
38406     /**
38407      * Set the tooltip for the tab.
38408      * @param {String} tooltip The tab's tooltip
38409      */
38410     setTooltip : function(text){
38411         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38412             this.textEl.dom.qtip = text;
38413             this.textEl.dom.removeAttribute('title');
38414         }else{
38415             this.textEl.dom.title = text;
38416         }
38417     },
38418
38419     onTabClick : function(e){
38420         e.preventDefault();
38421         this.tabPanel.activate(this.id);
38422     },
38423
38424     onTabMouseDown : function(e){
38425         e.preventDefault();
38426         this.tabPanel.activate(this.id);
38427     },
38428 /*
38429     getWidth : function(){
38430         return this.inner.getWidth();
38431     },
38432
38433     setWidth : function(width){
38434         var iwidth = width - this.pnode.getPadding("lr");
38435         this.inner.setWidth(iwidth);
38436         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38437         this.pnode.setWidth(width);
38438     },
38439 */
38440     /**
38441      * Show or hide the tab
38442      * @param {Boolean} hidden True to hide or false to show.
38443      */
38444     setHidden : function(hidden){
38445         this.hidden = hidden;
38446         this.pnode.setStyle("display", hidden ? "none" : "");
38447     },
38448
38449     /**
38450      * Returns true if this tab is "hidden"
38451      * @return {Boolean}
38452      */
38453     isHidden : function(){
38454         return this.hidden;
38455     },
38456
38457     /**
38458      * Returns the text for this tab
38459      * @return {String}
38460      */
38461     getText : function(){
38462         return this.text;
38463     },
38464     /*
38465     autoSize : function(){
38466         //this.el.beginMeasure();
38467         this.textEl.setWidth(1);
38468         /*
38469          *  #2804 [new] Tabs in Roojs
38470          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38471          */
38472         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38473         //this.el.endMeasure();
38474     //},
38475
38476     /**
38477      * Sets the text for the tab (Note: this also sets the tooltip text)
38478      * @param {String} text The tab's text and tooltip
38479      */
38480     setText : function(text){
38481         this.text = text;
38482         this.textEl.update(text);
38483         this.setTooltip(text);
38484         //if(!this.tabPanel.resizeTabs){
38485         //    this.autoSize();
38486         //}
38487     },
38488     /**
38489      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38490      */
38491     activate : function(){
38492         this.tabPanel.activate(this.id);
38493     },
38494
38495     /**
38496      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38497      */
38498     disable : function(){
38499         if(this.tabPanel.active != this){
38500             this.disabled = true;
38501             this.pnode.addClass("disabled");
38502         }
38503     },
38504
38505     /**
38506      * Enables this TabPanelItem if it was previously disabled.
38507      */
38508     enable : function(){
38509         this.disabled = false;
38510         this.pnode.removeClass("disabled");
38511     },
38512
38513     /**
38514      * Sets the content for this TabPanelItem.
38515      * @param {String} content The content
38516      * @param {Boolean} loadScripts true to look for and load scripts
38517      */
38518     setContent : function(content, loadScripts){
38519         this.bodyEl.update(content, loadScripts);
38520     },
38521
38522     /**
38523      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38524      * @return {Roo.UpdateManager} The UpdateManager
38525      */
38526     getUpdateManager : function(){
38527         return this.bodyEl.getUpdateManager();
38528     },
38529
38530     /**
38531      * Set a URL to be used to load the content for this TabPanelItem.
38532      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38533      * @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)
38534      * @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)
38535      * @return {Roo.UpdateManager} The UpdateManager
38536      */
38537     setUrl : function(url, params, loadOnce){
38538         if(this.refreshDelegate){
38539             this.un('activate', this.refreshDelegate);
38540         }
38541         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38542         this.on("activate", this.refreshDelegate);
38543         return this.bodyEl.getUpdateManager();
38544     },
38545
38546     /** @private */
38547     _handleRefresh : function(url, params, loadOnce){
38548         if(!loadOnce || !this.loaded){
38549             var updater = this.bodyEl.getUpdateManager();
38550             updater.update(url, params, this._setLoaded.createDelegate(this));
38551         }
38552     },
38553
38554     /**
38555      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38556      *   Will fail silently if the setUrl method has not been called.
38557      *   This does not activate the panel, just updates its content.
38558      */
38559     refresh : function(){
38560         if(this.refreshDelegate){
38561            this.loaded = false;
38562            this.refreshDelegate();
38563         }
38564     },
38565
38566     /** @private */
38567     _setLoaded : function(){
38568         this.loaded = true;
38569     },
38570
38571     /** @private */
38572     closeClick : function(e){
38573         var o = {};
38574         e.stopEvent();
38575         this.fireEvent("beforeclose", this, o);
38576         if(o.cancel !== true){
38577             this.tabPanel.removeTab(this.id);
38578         }
38579     },
38580     /**
38581      * The text displayed in the tooltip for the close icon.
38582      * @type String
38583      */
38584     closeText : "Close this tab"
38585 });
38586 /**
38587 *    This script refer to:
38588 *    Title: International Telephone Input
38589 *    Author: Jack O'Connor
38590 *    Code version:  v12.1.12
38591 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38592 **/
38593
38594 Roo.bootstrap.PhoneInputData = function() {
38595     var d = [
38596       [
38597         "Afghanistan (‫افغانستان‬‎)",
38598         "af",
38599         "93"
38600       ],
38601       [
38602         "Albania (Shqipëri)",
38603         "al",
38604         "355"
38605       ],
38606       [
38607         "Algeria (‫الجزائر‬‎)",
38608         "dz",
38609         "213"
38610       ],
38611       [
38612         "American Samoa",
38613         "as",
38614         "1684"
38615       ],
38616       [
38617         "Andorra",
38618         "ad",
38619         "376"
38620       ],
38621       [
38622         "Angola",
38623         "ao",
38624         "244"
38625       ],
38626       [
38627         "Anguilla",
38628         "ai",
38629         "1264"
38630       ],
38631       [
38632         "Antigua and Barbuda",
38633         "ag",
38634         "1268"
38635       ],
38636       [
38637         "Argentina",
38638         "ar",
38639         "54"
38640       ],
38641       [
38642         "Armenia (Հայաստան)",
38643         "am",
38644         "374"
38645       ],
38646       [
38647         "Aruba",
38648         "aw",
38649         "297"
38650       ],
38651       [
38652         "Australia",
38653         "au",
38654         "61",
38655         0
38656       ],
38657       [
38658         "Austria (Österreich)",
38659         "at",
38660         "43"
38661       ],
38662       [
38663         "Azerbaijan (Azərbaycan)",
38664         "az",
38665         "994"
38666       ],
38667       [
38668         "Bahamas",
38669         "bs",
38670         "1242"
38671       ],
38672       [
38673         "Bahrain (‫البحرين‬‎)",
38674         "bh",
38675         "973"
38676       ],
38677       [
38678         "Bangladesh (বাংলাদেশ)",
38679         "bd",
38680         "880"
38681       ],
38682       [
38683         "Barbados",
38684         "bb",
38685         "1246"
38686       ],
38687       [
38688         "Belarus (Беларусь)",
38689         "by",
38690         "375"
38691       ],
38692       [
38693         "Belgium (België)",
38694         "be",
38695         "32"
38696       ],
38697       [
38698         "Belize",
38699         "bz",
38700         "501"
38701       ],
38702       [
38703         "Benin (Bénin)",
38704         "bj",
38705         "229"
38706       ],
38707       [
38708         "Bermuda",
38709         "bm",
38710         "1441"
38711       ],
38712       [
38713         "Bhutan (འབྲུག)",
38714         "bt",
38715         "975"
38716       ],
38717       [
38718         "Bolivia",
38719         "bo",
38720         "591"
38721       ],
38722       [
38723         "Bosnia and Herzegovina (Босна и Херцеговина)",
38724         "ba",
38725         "387"
38726       ],
38727       [
38728         "Botswana",
38729         "bw",
38730         "267"
38731       ],
38732       [
38733         "Brazil (Brasil)",
38734         "br",
38735         "55"
38736       ],
38737       [
38738         "British Indian Ocean Territory",
38739         "io",
38740         "246"
38741       ],
38742       [
38743         "British Virgin Islands",
38744         "vg",
38745         "1284"
38746       ],
38747       [
38748         "Brunei",
38749         "bn",
38750         "673"
38751       ],
38752       [
38753         "Bulgaria (България)",
38754         "bg",
38755         "359"
38756       ],
38757       [
38758         "Burkina Faso",
38759         "bf",
38760         "226"
38761       ],
38762       [
38763         "Burundi (Uburundi)",
38764         "bi",
38765         "257"
38766       ],
38767       [
38768         "Cambodia (កម្ពុជា)",
38769         "kh",
38770         "855"
38771       ],
38772       [
38773         "Cameroon (Cameroun)",
38774         "cm",
38775         "237"
38776       ],
38777       [
38778         "Canada",
38779         "ca",
38780         "1",
38781         1,
38782         ["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"]
38783       ],
38784       [
38785         "Cape Verde (Kabu Verdi)",
38786         "cv",
38787         "238"
38788       ],
38789       [
38790         "Caribbean Netherlands",
38791         "bq",
38792         "599",
38793         1
38794       ],
38795       [
38796         "Cayman Islands",
38797         "ky",
38798         "1345"
38799       ],
38800       [
38801         "Central African Republic (République centrafricaine)",
38802         "cf",
38803         "236"
38804       ],
38805       [
38806         "Chad (Tchad)",
38807         "td",
38808         "235"
38809       ],
38810       [
38811         "Chile",
38812         "cl",
38813         "56"
38814       ],
38815       [
38816         "China (中国)",
38817         "cn",
38818         "86"
38819       ],
38820       [
38821         "Christmas Island",
38822         "cx",
38823         "61",
38824         2
38825       ],
38826       [
38827         "Cocos (Keeling) Islands",
38828         "cc",
38829         "61",
38830         1
38831       ],
38832       [
38833         "Colombia",
38834         "co",
38835         "57"
38836       ],
38837       [
38838         "Comoros (‫جزر القمر‬‎)",
38839         "km",
38840         "269"
38841       ],
38842       [
38843         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38844         "cd",
38845         "243"
38846       ],
38847       [
38848         "Congo (Republic) (Congo-Brazzaville)",
38849         "cg",
38850         "242"
38851       ],
38852       [
38853         "Cook Islands",
38854         "ck",
38855         "682"
38856       ],
38857       [
38858         "Costa Rica",
38859         "cr",
38860         "506"
38861       ],
38862       [
38863         "Côte d’Ivoire",
38864         "ci",
38865         "225"
38866       ],
38867       [
38868         "Croatia (Hrvatska)",
38869         "hr",
38870         "385"
38871       ],
38872       [
38873         "Cuba",
38874         "cu",
38875         "53"
38876       ],
38877       [
38878         "Curaçao",
38879         "cw",
38880         "599",
38881         0
38882       ],
38883       [
38884         "Cyprus (Κύπρος)",
38885         "cy",
38886         "357"
38887       ],
38888       [
38889         "Czech Republic (Česká republika)",
38890         "cz",
38891         "420"
38892       ],
38893       [
38894         "Denmark (Danmark)",
38895         "dk",
38896         "45"
38897       ],
38898       [
38899         "Djibouti",
38900         "dj",
38901         "253"
38902       ],
38903       [
38904         "Dominica",
38905         "dm",
38906         "1767"
38907       ],
38908       [
38909         "Dominican Republic (República Dominicana)",
38910         "do",
38911         "1",
38912         2,
38913         ["809", "829", "849"]
38914       ],
38915       [
38916         "Ecuador",
38917         "ec",
38918         "593"
38919       ],
38920       [
38921         "Egypt (‫مصر‬‎)",
38922         "eg",
38923         "20"
38924       ],
38925       [
38926         "El Salvador",
38927         "sv",
38928         "503"
38929       ],
38930       [
38931         "Equatorial Guinea (Guinea Ecuatorial)",
38932         "gq",
38933         "240"
38934       ],
38935       [
38936         "Eritrea",
38937         "er",
38938         "291"
38939       ],
38940       [
38941         "Estonia (Eesti)",
38942         "ee",
38943         "372"
38944       ],
38945       [
38946         "Ethiopia",
38947         "et",
38948         "251"
38949       ],
38950       [
38951         "Falkland Islands (Islas Malvinas)",
38952         "fk",
38953         "500"
38954       ],
38955       [
38956         "Faroe Islands (Føroyar)",
38957         "fo",
38958         "298"
38959       ],
38960       [
38961         "Fiji",
38962         "fj",
38963         "679"
38964       ],
38965       [
38966         "Finland (Suomi)",
38967         "fi",
38968         "358",
38969         0
38970       ],
38971       [
38972         "France",
38973         "fr",
38974         "33"
38975       ],
38976       [
38977         "French Guiana (Guyane française)",
38978         "gf",
38979         "594"
38980       ],
38981       [
38982         "French Polynesia (Polynésie française)",
38983         "pf",
38984         "689"
38985       ],
38986       [
38987         "Gabon",
38988         "ga",
38989         "241"
38990       ],
38991       [
38992         "Gambia",
38993         "gm",
38994         "220"
38995       ],
38996       [
38997         "Georgia (საქართველო)",
38998         "ge",
38999         "995"
39000       ],
39001       [
39002         "Germany (Deutschland)",
39003         "de",
39004         "49"
39005       ],
39006       [
39007         "Ghana (Gaana)",
39008         "gh",
39009         "233"
39010       ],
39011       [
39012         "Gibraltar",
39013         "gi",
39014         "350"
39015       ],
39016       [
39017         "Greece (Ελλάδα)",
39018         "gr",
39019         "30"
39020       ],
39021       [
39022         "Greenland (Kalaallit Nunaat)",
39023         "gl",
39024         "299"
39025       ],
39026       [
39027         "Grenada",
39028         "gd",
39029         "1473"
39030       ],
39031       [
39032         "Guadeloupe",
39033         "gp",
39034         "590",
39035         0
39036       ],
39037       [
39038         "Guam",
39039         "gu",
39040         "1671"
39041       ],
39042       [
39043         "Guatemala",
39044         "gt",
39045         "502"
39046       ],
39047       [
39048         "Guernsey",
39049         "gg",
39050         "44",
39051         1
39052       ],
39053       [
39054         "Guinea (Guinée)",
39055         "gn",
39056         "224"
39057       ],
39058       [
39059         "Guinea-Bissau (Guiné Bissau)",
39060         "gw",
39061         "245"
39062       ],
39063       [
39064         "Guyana",
39065         "gy",
39066         "592"
39067       ],
39068       [
39069         "Haiti",
39070         "ht",
39071         "509"
39072       ],
39073       [
39074         "Honduras",
39075         "hn",
39076         "504"
39077       ],
39078       [
39079         "Hong Kong (香港)",
39080         "hk",
39081         "852"
39082       ],
39083       [
39084         "Hungary (Magyarország)",
39085         "hu",
39086         "36"
39087       ],
39088       [
39089         "Iceland (Ísland)",
39090         "is",
39091         "354"
39092       ],
39093       [
39094         "India (भारत)",
39095         "in",
39096         "91"
39097       ],
39098       [
39099         "Indonesia",
39100         "id",
39101         "62"
39102       ],
39103       [
39104         "Iran (‫ایران‬‎)",
39105         "ir",
39106         "98"
39107       ],
39108       [
39109         "Iraq (‫العراق‬‎)",
39110         "iq",
39111         "964"
39112       ],
39113       [
39114         "Ireland",
39115         "ie",
39116         "353"
39117       ],
39118       [
39119         "Isle of Man",
39120         "im",
39121         "44",
39122         2
39123       ],
39124       [
39125         "Israel (‫ישראל‬‎)",
39126         "il",
39127         "972"
39128       ],
39129       [
39130         "Italy (Italia)",
39131         "it",
39132         "39",
39133         0
39134       ],
39135       [
39136         "Jamaica",
39137         "jm",
39138         "1876"
39139       ],
39140       [
39141         "Japan (日本)",
39142         "jp",
39143         "81"
39144       ],
39145       [
39146         "Jersey",
39147         "je",
39148         "44",
39149         3
39150       ],
39151       [
39152         "Jordan (‫الأردن‬‎)",
39153         "jo",
39154         "962"
39155       ],
39156       [
39157         "Kazakhstan (Казахстан)",
39158         "kz",
39159         "7",
39160         1
39161       ],
39162       [
39163         "Kenya",
39164         "ke",
39165         "254"
39166       ],
39167       [
39168         "Kiribati",
39169         "ki",
39170         "686"
39171       ],
39172       [
39173         "Kosovo",
39174         "xk",
39175         "383"
39176       ],
39177       [
39178         "Kuwait (‫الكويت‬‎)",
39179         "kw",
39180         "965"
39181       ],
39182       [
39183         "Kyrgyzstan (Кыргызстан)",
39184         "kg",
39185         "996"
39186       ],
39187       [
39188         "Laos (ລາວ)",
39189         "la",
39190         "856"
39191       ],
39192       [
39193         "Latvia (Latvija)",
39194         "lv",
39195         "371"
39196       ],
39197       [
39198         "Lebanon (‫لبنان‬‎)",
39199         "lb",
39200         "961"
39201       ],
39202       [
39203         "Lesotho",
39204         "ls",
39205         "266"
39206       ],
39207       [
39208         "Liberia",
39209         "lr",
39210         "231"
39211       ],
39212       [
39213         "Libya (‫ليبيا‬‎)",
39214         "ly",
39215         "218"
39216       ],
39217       [
39218         "Liechtenstein",
39219         "li",
39220         "423"
39221       ],
39222       [
39223         "Lithuania (Lietuva)",
39224         "lt",
39225         "370"
39226       ],
39227       [
39228         "Luxembourg",
39229         "lu",
39230         "352"
39231       ],
39232       [
39233         "Macau (澳門)",
39234         "mo",
39235         "853"
39236       ],
39237       [
39238         "Macedonia (FYROM) (Македонија)",
39239         "mk",
39240         "389"
39241       ],
39242       [
39243         "Madagascar (Madagasikara)",
39244         "mg",
39245         "261"
39246       ],
39247       [
39248         "Malawi",
39249         "mw",
39250         "265"
39251       ],
39252       [
39253         "Malaysia",
39254         "my",
39255         "60"
39256       ],
39257       [
39258         "Maldives",
39259         "mv",
39260         "960"
39261       ],
39262       [
39263         "Mali",
39264         "ml",
39265         "223"
39266       ],
39267       [
39268         "Malta",
39269         "mt",
39270         "356"
39271       ],
39272       [
39273         "Marshall Islands",
39274         "mh",
39275         "692"
39276       ],
39277       [
39278         "Martinique",
39279         "mq",
39280         "596"
39281       ],
39282       [
39283         "Mauritania (‫موريتانيا‬‎)",
39284         "mr",
39285         "222"
39286       ],
39287       [
39288         "Mauritius (Moris)",
39289         "mu",
39290         "230"
39291       ],
39292       [
39293         "Mayotte",
39294         "yt",
39295         "262",
39296         1
39297       ],
39298       [
39299         "Mexico (México)",
39300         "mx",
39301         "52"
39302       ],
39303       [
39304         "Micronesia",
39305         "fm",
39306         "691"
39307       ],
39308       [
39309         "Moldova (Republica Moldova)",
39310         "md",
39311         "373"
39312       ],
39313       [
39314         "Monaco",
39315         "mc",
39316         "377"
39317       ],
39318       [
39319         "Mongolia (Монгол)",
39320         "mn",
39321         "976"
39322       ],
39323       [
39324         "Montenegro (Crna Gora)",
39325         "me",
39326         "382"
39327       ],
39328       [
39329         "Montserrat",
39330         "ms",
39331         "1664"
39332       ],
39333       [
39334         "Morocco (‫المغرب‬‎)",
39335         "ma",
39336         "212",
39337         0
39338       ],
39339       [
39340         "Mozambique (Moçambique)",
39341         "mz",
39342         "258"
39343       ],
39344       [
39345         "Myanmar (Burma) (မြန်မာ)",
39346         "mm",
39347         "95"
39348       ],
39349       [
39350         "Namibia (Namibië)",
39351         "na",
39352         "264"
39353       ],
39354       [
39355         "Nauru",
39356         "nr",
39357         "674"
39358       ],
39359       [
39360         "Nepal (नेपाल)",
39361         "np",
39362         "977"
39363       ],
39364       [
39365         "Netherlands (Nederland)",
39366         "nl",
39367         "31"
39368       ],
39369       [
39370         "New Caledonia (Nouvelle-Calédonie)",
39371         "nc",
39372         "687"
39373       ],
39374       [
39375         "New Zealand",
39376         "nz",
39377         "64"
39378       ],
39379       [
39380         "Nicaragua",
39381         "ni",
39382         "505"
39383       ],
39384       [
39385         "Niger (Nijar)",
39386         "ne",
39387         "227"
39388       ],
39389       [
39390         "Nigeria",
39391         "ng",
39392         "234"
39393       ],
39394       [
39395         "Niue",
39396         "nu",
39397         "683"
39398       ],
39399       [
39400         "Norfolk Island",
39401         "nf",
39402         "672"
39403       ],
39404       [
39405         "North Korea (조선 민주주의 인민 공화국)",
39406         "kp",
39407         "850"
39408       ],
39409       [
39410         "Northern Mariana Islands",
39411         "mp",
39412         "1670"
39413       ],
39414       [
39415         "Norway (Norge)",
39416         "no",
39417         "47",
39418         0
39419       ],
39420       [
39421         "Oman (‫عُمان‬‎)",
39422         "om",
39423         "968"
39424       ],
39425       [
39426         "Pakistan (‫پاکستان‬‎)",
39427         "pk",
39428         "92"
39429       ],
39430       [
39431         "Palau",
39432         "pw",
39433         "680"
39434       ],
39435       [
39436         "Palestine (‫فلسطين‬‎)",
39437         "ps",
39438         "970"
39439       ],
39440       [
39441         "Panama (Panamá)",
39442         "pa",
39443         "507"
39444       ],
39445       [
39446         "Papua New Guinea",
39447         "pg",
39448         "675"
39449       ],
39450       [
39451         "Paraguay",
39452         "py",
39453         "595"
39454       ],
39455       [
39456         "Peru (Perú)",
39457         "pe",
39458         "51"
39459       ],
39460       [
39461         "Philippines",
39462         "ph",
39463         "63"
39464       ],
39465       [
39466         "Poland (Polska)",
39467         "pl",
39468         "48"
39469       ],
39470       [
39471         "Portugal",
39472         "pt",
39473         "351"
39474       ],
39475       [
39476         "Puerto Rico",
39477         "pr",
39478         "1",
39479         3,
39480         ["787", "939"]
39481       ],
39482       [
39483         "Qatar (‫قطر‬‎)",
39484         "qa",
39485         "974"
39486       ],
39487       [
39488         "Réunion (La Réunion)",
39489         "re",
39490         "262",
39491         0
39492       ],
39493       [
39494         "Romania (România)",
39495         "ro",
39496         "40"
39497       ],
39498       [
39499         "Russia (Россия)",
39500         "ru",
39501         "7",
39502         0
39503       ],
39504       [
39505         "Rwanda",
39506         "rw",
39507         "250"
39508       ],
39509       [
39510         "Saint Barthélemy",
39511         "bl",
39512         "590",
39513         1
39514       ],
39515       [
39516         "Saint Helena",
39517         "sh",
39518         "290"
39519       ],
39520       [
39521         "Saint Kitts and Nevis",
39522         "kn",
39523         "1869"
39524       ],
39525       [
39526         "Saint Lucia",
39527         "lc",
39528         "1758"
39529       ],
39530       [
39531         "Saint Martin (Saint-Martin (partie française))",
39532         "mf",
39533         "590",
39534         2
39535       ],
39536       [
39537         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39538         "pm",
39539         "508"
39540       ],
39541       [
39542         "Saint Vincent and the Grenadines",
39543         "vc",
39544         "1784"
39545       ],
39546       [
39547         "Samoa",
39548         "ws",
39549         "685"
39550       ],
39551       [
39552         "San Marino",
39553         "sm",
39554         "378"
39555       ],
39556       [
39557         "São Tomé and Príncipe (São Tomé e Príncipe)",
39558         "st",
39559         "239"
39560       ],
39561       [
39562         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39563         "sa",
39564         "966"
39565       ],
39566       [
39567         "Senegal (Sénégal)",
39568         "sn",
39569         "221"
39570       ],
39571       [
39572         "Serbia (Србија)",
39573         "rs",
39574         "381"
39575       ],
39576       [
39577         "Seychelles",
39578         "sc",
39579         "248"
39580       ],
39581       [
39582         "Sierra Leone",
39583         "sl",
39584         "232"
39585       ],
39586       [
39587         "Singapore",
39588         "sg",
39589         "65"
39590       ],
39591       [
39592         "Sint Maarten",
39593         "sx",
39594         "1721"
39595       ],
39596       [
39597         "Slovakia (Slovensko)",
39598         "sk",
39599         "421"
39600       ],
39601       [
39602         "Slovenia (Slovenija)",
39603         "si",
39604         "386"
39605       ],
39606       [
39607         "Solomon Islands",
39608         "sb",
39609         "677"
39610       ],
39611       [
39612         "Somalia (Soomaaliya)",
39613         "so",
39614         "252"
39615       ],
39616       [
39617         "South Africa",
39618         "za",
39619         "27"
39620       ],
39621       [
39622         "South Korea (대한민국)",
39623         "kr",
39624         "82"
39625       ],
39626       [
39627         "South Sudan (‫جنوب السودان‬‎)",
39628         "ss",
39629         "211"
39630       ],
39631       [
39632         "Spain (España)",
39633         "es",
39634         "34"
39635       ],
39636       [
39637         "Sri Lanka (ශ්‍රී ලංකාව)",
39638         "lk",
39639         "94"
39640       ],
39641       [
39642         "Sudan (‫السودان‬‎)",
39643         "sd",
39644         "249"
39645       ],
39646       [
39647         "Suriname",
39648         "sr",
39649         "597"
39650       ],
39651       [
39652         "Svalbard and Jan Mayen",
39653         "sj",
39654         "47",
39655         1
39656       ],
39657       [
39658         "Swaziland",
39659         "sz",
39660         "268"
39661       ],
39662       [
39663         "Sweden (Sverige)",
39664         "se",
39665         "46"
39666       ],
39667       [
39668         "Switzerland (Schweiz)",
39669         "ch",
39670         "41"
39671       ],
39672       [
39673         "Syria (‫سوريا‬‎)",
39674         "sy",
39675         "963"
39676       ],
39677       [
39678         "Taiwan (台灣)",
39679         "tw",
39680         "886"
39681       ],
39682       [
39683         "Tajikistan",
39684         "tj",
39685         "992"
39686       ],
39687       [
39688         "Tanzania",
39689         "tz",
39690         "255"
39691       ],
39692       [
39693         "Thailand (ไทย)",
39694         "th",
39695         "66"
39696       ],
39697       [
39698         "Timor-Leste",
39699         "tl",
39700         "670"
39701       ],
39702       [
39703         "Togo",
39704         "tg",
39705         "228"
39706       ],
39707       [
39708         "Tokelau",
39709         "tk",
39710         "690"
39711       ],
39712       [
39713         "Tonga",
39714         "to",
39715         "676"
39716       ],
39717       [
39718         "Trinidad and Tobago",
39719         "tt",
39720         "1868"
39721       ],
39722       [
39723         "Tunisia (‫تونس‬‎)",
39724         "tn",
39725         "216"
39726       ],
39727       [
39728         "Turkey (Türkiye)",
39729         "tr",
39730         "90"
39731       ],
39732       [
39733         "Turkmenistan",
39734         "tm",
39735         "993"
39736       ],
39737       [
39738         "Turks and Caicos Islands",
39739         "tc",
39740         "1649"
39741       ],
39742       [
39743         "Tuvalu",
39744         "tv",
39745         "688"
39746       ],
39747       [
39748         "U.S. Virgin Islands",
39749         "vi",
39750         "1340"
39751       ],
39752       [
39753         "Uganda",
39754         "ug",
39755         "256"
39756       ],
39757       [
39758         "Ukraine (Україна)",
39759         "ua",
39760         "380"
39761       ],
39762       [
39763         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39764         "ae",
39765         "971"
39766       ],
39767       [
39768         "United Kingdom",
39769         "gb",
39770         "44",
39771         0
39772       ],
39773       [
39774         "United States",
39775         "us",
39776         "1",
39777         0
39778       ],
39779       [
39780         "Uruguay",
39781         "uy",
39782         "598"
39783       ],
39784       [
39785         "Uzbekistan (Oʻzbekiston)",
39786         "uz",
39787         "998"
39788       ],
39789       [
39790         "Vanuatu",
39791         "vu",
39792         "678"
39793       ],
39794       [
39795         "Vatican City (Città del Vaticano)",
39796         "va",
39797         "39",
39798         1
39799       ],
39800       [
39801         "Venezuela",
39802         "ve",
39803         "58"
39804       ],
39805       [
39806         "Vietnam (Việt Nam)",
39807         "vn",
39808         "84"
39809       ],
39810       [
39811         "Wallis and Futuna (Wallis-et-Futuna)",
39812         "wf",
39813         "681"
39814       ],
39815       [
39816         "Western Sahara (‫الصحراء الغربية‬‎)",
39817         "eh",
39818         "212",
39819         1
39820       ],
39821       [
39822         "Yemen (‫اليمن‬‎)",
39823         "ye",
39824         "967"
39825       ],
39826       [
39827         "Zambia",
39828         "zm",
39829         "260"
39830       ],
39831       [
39832         "Zimbabwe",
39833         "zw",
39834         "263"
39835       ],
39836       [
39837         "Åland Islands",
39838         "ax",
39839         "358",
39840         1
39841       ]
39842   ];
39843   
39844   return d;
39845 }/**
39846 *    This script refer to:
39847 *    Title: International Telephone Input
39848 *    Author: Jack O'Connor
39849 *    Code version:  v12.1.12
39850 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39851 **/
39852
39853 /**
39854  * @class Roo.bootstrap.PhoneInput
39855  * @extends Roo.bootstrap.TriggerField
39856  * An input with International dial-code selection
39857  
39858  * @cfg {String} defaultDialCode default '+852'
39859  * @cfg {Array} preferedCountries default []
39860   
39861  * @constructor
39862  * Create a new PhoneInput.
39863  * @param {Object} config Configuration options
39864  */
39865
39866 Roo.bootstrap.PhoneInput = function(config) {
39867     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39868 };
39869
39870 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39871         
39872         listWidth: undefined,
39873         
39874         selectedClass: 'active',
39875         
39876         invalidClass : "has-warning",
39877         
39878         validClass: 'has-success',
39879         
39880         allowed: '0123456789',
39881         
39882         max_length: 15,
39883         
39884         /**
39885          * @cfg {String} defaultDialCode The default dial code when initializing the input
39886          */
39887         defaultDialCode: '+852',
39888         
39889         /**
39890          * @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
39891          */
39892         preferedCountries: false,
39893         
39894         getAutoCreate : function()
39895         {
39896             var data = Roo.bootstrap.PhoneInputData();
39897             var align = this.labelAlign || this.parentLabelAlign();
39898             var id = Roo.id();
39899             
39900             this.allCountries = [];
39901             this.dialCodeMapping = [];
39902             
39903             for (var i = 0; i < data.length; i++) {
39904               var c = data[i];
39905               this.allCountries[i] = {
39906                 name: c[0],
39907                 iso2: c[1],
39908                 dialCode: c[2],
39909                 priority: c[3] || 0,
39910                 areaCodes: c[4] || null
39911               };
39912               this.dialCodeMapping[c[2]] = {
39913                   name: c[0],
39914                   iso2: c[1],
39915                   priority: c[3] || 0,
39916                   areaCodes: c[4] || null
39917               };
39918             }
39919             
39920             var cfg = {
39921                 cls: 'form-group',
39922                 cn: []
39923             };
39924             
39925             var input =  {
39926                 tag: 'input',
39927                 id : id,
39928                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39929                 maxlength: this.max_length,
39930                 cls : 'form-control tel-input',
39931                 autocomplete: 'new-password'
39932             };
39933             
39934             var hiddenInput = {
39935                 tag: 'input',
39936                 type: 'hidden',
39937                 cls: 'hidden-tel-input'
39938             };
39939             
39940             if (this.name) {
39941                 hiddenInput.name = this.name;
39942             }
39943             
39944             if (this.disabled) {
39945                 input.disabled = true;
39946             }
39947             
39948             var flag_container = {
39949                 tag: 'div',
39950                 cls: 'flag-box',
39951                 cn: [
39952                     {
39953                         tag: 'div',
39954                         cls: 'flag'
39955                     },
39956                     {
39957                         tag: 'div',
39958                         cls: 'caret'
39959                     }
39960                 ]
39961             };
39962             
39963             var box = {
39964                 tag: 'div',
39965                 cls: this.hasFeedback ? 'has-feedback' : '',
39966                 cn: [
39967                     hiddenInput,
39968                     input,
39969                     {
39970                         tag: 'input',
39971                         cls: 'dial-code-holder',
39972                         disabled: true
39973                     }
39974                 ]
39975             };
39976             
39977             var container = {
39978                 cls: 'roo-select2-container input-group',
39979                 cn: [
39980                     flag_container,
39981                     box
39982                 ]
39983             };
39984             
39985             if (this.fieldLabel.length) {
39986                 var indicator = {
39987                     tag: 'i',
39988                     tooltip: 'This field is required'
39989                 };
39990                 
39991                 var label = {
39992                     tag: 'label',
39993                     'for':  id,
39994                     cls: 'control-label',
39995                     cn: []
39996                 };
39997                 
39998                 var label_text = {
39999                     tag: 'span',
40000                     html: this.fieldLabel
40001                 };
40002                 
40003                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40004                 label.cn = [
40005                     indicator,
40006                     label_text
40007                 ];
40008                 
40009                 if(this.indicatorpos == 'right') {
40010                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40011                     label.cn = [
40012                         label_text,
40013                         indicator
40014                     ];
40015                 }
40016                 
40017                 if(align == 'left') {
40018                     container = {
40019                         tag: 'div',
40020                         cn: [
40021                             container
40022                         ]
40023                     };
40024                     
40025                     if(this.labelWidth > 12){
40026                         label.style = "width: " + this.labelWidth + 'px';
40027                     }
40028                     if(this.labelWidth < 13 && this.labelmd == 0){
40029                         this.labelmd = this.labelWidth;
40030                     }
40031                     if(this.labellg > 0){
40032                         label.cls += ' col-lg-' + this.labellg;
40033                         input.cls += ' col-lg-' + (12 - this.labellg);
40034                     }
40035                     if(this.labelmd > 0){
40036                         label.cls += ' col-md-' + this.labelmd;
40037                         container.cls += ' col-md-' + (12 - this.labelmd);
40038                     }
40039                     if(this.labelsm > 0){
40040                         label.cls += ' col-sm-' + this.labelsm;
40041                         container.cls += ' col-sm-' + (12 - this.labelsm);
40042                     }
40043                     if(this.labelxs > 0){
40044                         label.cls += ' col-xs-' + this.labelxs;
40045                         container.cls += ' col-xs-' + (12 - this.labelxs);
40046                     }
40047                 }
40048             }
40049             
40050             cfg.cn = [
40051                 label,
40052                 container
40053             ];
40054             
40055             var settings = this;
40056             
40057             ['xs','sm','md','lg'].map(function(size){
40058                 if (settings[size]) {
40059                     cfg.cls += ' col-' + size + '-' + settings[size];
40060                 }
40061             });
40062             
40063             this.store = new Roo.data.Store({
40064                 proxy : new Roo.data.MemoryProxy({}),
40065                 reader : new Roo.data.JsonReader({
40066                     fields : [
40067                         {
40068                             'name' : 'name',
40069                             'type' : 'string'
40070                         },
40071                         {
40072                             'name' : 'iso2',
40073                             'type' : 'string'
40074                         },
40075                         {
40076                             'name' : 'dialCode',
40077                             'type' : 'string'
40078                         },
40079                         {
40080                             'name' : 'priority',
40081                             'type' : 'string'
40082                         },
40083                         {
40084                             'name' : 'areaCodes',
40085                             'type' : 'string'
40086                         }
40087                     ]
40088                 })
40089             });
40090             
40091             if(!this.preferedCountries) {
40092                 this.preferedCountries = [
40093                     'hk',
40094                     'gb',
40095                     'us'
40096                 ];
40097             }
40098             
40099             var p = this.preferedCountries.reverse();
40100             
40101             if(p) {
40102                 for (var i = 0; i < p.length; i++) {
40103                     for (var j = 0; j < this.allCountries.length; j++) {
40104                         if(this.allCountries[j].iso2 == p[i]) {
40105                             var t = this.allCountries[j];
40106                             this.allCountries.splice(j,1);
40107                             this.allCountries.unshift(t);
40108                         }
40109                     } 
40110                 }
40111             }
40112             
40113             this.store.proxy.data = {
40114                 success: true,
40115                 data: this.allCountries
40116             };
40117             
40118             return cfg;
40119         },
40120         
40121         initEvents : function()
40122         {
40123             this.createList();
40124             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40125             
40126             this.indicator = this.indicatorEl();
40127             this.flag = this.flagEl();
40128             this.dialCodeHolder = this.dialCodeHolderEl();
40129             
40130             this.trigger = this.el.select('div.flag-box',true).first();
40131             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40132             
40133             var _this = this;
40134             
40135             (function(){
40136                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40137                 _this.list.setWidth(lw);
40138             }).defer(100);
40139             
40140             this.list.on('mouseover', this.onViewOver, this);
40141             this.list.on('mousemove', this.onViewMove, this);
40142             this.inputEl().on("keyup", this.onKeyUp, this);
40143             this.inputEl().on("keypress", this.onKeyPress, this);
40144             
40145             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40146
40147             this.view = new Roo.View(this.list, this.tpl, {
40148                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40149             });
40150             
40151             this.view.on('click', this.onViewClick, this);
40152             this.setValue(this.defaultDialCode);
40153         },
40154         
40155         onTriggerClick : function(e)
40156         {
40157             Roo.log('trigger click');
40158             if(this.disabled){
40159                 return;
40160             }
40161             
40162             if(this.isExpanded()){
40163                 this.collapse();
40164                 this.hasFocus = false;
40165             }else {
40166                 this.store.load({});
40167                 this.hasFocus = true;
40168                 this.expand();
40169             }
40170         },
40171         
40172         isExpanded : function()
40173         {
40174             return this.list.isVisible();
40175         },
40176         
40177         collapse : function()
40178         {
40179             if(!this.isExpanded()){
40180                 return;
40181             }
40182             this.list.hide();
40183             Roo.get(document).un('mousedown', this.collapseIf, this);
40184             Roo.get(document).un('mousewheel', this.collapseIf, this);
40185             this.fireEvent('collapse', this);
40186             this.validate();
40187         },
40188         
40189         expand : function()
40190         {
40191             Roo.log('expand');
40192
40193             if(this.isExpanded() || !this.hasFocus){
40194                 return;
40195             }
40196             
40197             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40198             this.list.setWidth(lw);
40199             
40200             this.list.show();
40201             this.restrictHeight();
40202             
40203             Roo.get(document).on('mousedown', this.collapseIf, this);
40204             Roo.get(document).on('mousewheel', this.collapseIf, this);
40205             
40206             this.fireEvent('expand', this);
40207         },
40208         
40209         restrictHeight : function()
40210         {
40211             this.list.alignTo(this.inputEl(), this.listAlign);
40212             this.list.alignTo(this.inputEl(), this.listAlign);
40213         },
40214         
40215         onViewOver : function(e, t)
40216         {
40217             if(this.inKeyMode){
40218                 return;
40219             }
40220             var item = this.view.findItemFromChild(t);
40221             
40222             if(item){
40223                 var index = this.view.indexOf(item);
40224                 this.select(index, false);
40225             }
40226         },
40227
40228         // private
40229         onViewClick : function(view, doFocus, el, e)
40230         {
40231             var index = this.view.getSelectedIndexes()[0];
40232             
40233             var r = this.store.getAt(index);
40234             
40235             if(r){
40236                 this.onSelect(r, index);
40237             }
40238             if(doFocus !== false && !this.blockFocus){
40239                 this.inputEl().focus();
40240             }
40241         },
40242         
40243         onViewMove : function(e, t)
40244         {
40245             this.inKeyMode = false;
40246         },
40247         
40248         select : function(index, scrollIntoView)
40249         {
40250             this.selectedIndex = index;
40251             this.view.select(index);
40252             if(scrollIntoView !== false){
40253                 var el = this.view.getNode(index);
40254                 if(el){
40255                     this.list.scrollChildIntoView(el, false);
40256                 }
40257             }
40258         },
40259         
40260         createList : function()
40261         {
40262             this.list = Roo.get(document.body).createChild({
40263                 tag: 'ul',
40264                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40265                 style: 'display:none'
40266             });
40267             
40268             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40269         },
40270         
40271         collapseIf : function(e)
40272         {
40273             var in_combo  = e.within(this.el);
40274             var in_list =  e.within(this.list);
40275             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40276             
40277             if (in_combo || in_list || is_list) {
40278                 return;
40279             }
40280             this.collapse();
40281         },
40282         
40283         onSelect : function(record, index)
40284         {
40285             if(this.fireEvent('beforeselect', this, record, index) !== false){
40286                 
40287                 this.setFlagClass(record.data.iso2);
40288                 this.setDialCode(record.data.dialCode);
40289                 this.hasFocus = false;
40290                 this.collapse();
40291                 this.fireEvent('select', this, record, index);
40292             }
40293         },
40294         
40295         flagEl : function()
40296         {
40297             var flag = this.el.select('div.flag',true).first();
40298             if(!flag){
40299                 return false;
40300             }
40301             return flag;
40302         },
40303         
40304         dialCodeHolderEl : function()
40305         {
40306             var d = this.el.select('input.dial-code-holder',true).first();
40307             if(!d){
40308                 return false;
40309             }
40310             return d;
40311         },
40312         
40313         setDialCode : function(v)
40314         {
40315             this.dialCodeHolder.dom.value = '+'+v;
40316         },
40317         
40318         setFlagClass : function(n)
40319         {
40320             this.flag.dom.className = 'flag '+n;
40321         },
40322         
40323         getValue : function()
40324         {
40325             var v = this.inputEl().getValue();
40326             if(this.dialCodeHolder) {
40327                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40328             }
40329             return v;
40330         },
40331         
40332         setValue : function(v)
40333         {
40334             var d = this.getDialCode(v);
40335             
40336             //invalid dial code
40337             if(v.length == 0 || !d || d.length == 0) {
40338                 if(this.rendered){
40339                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40340                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40341                 }
40342                 return;
40343             }
40344             
40345             //valid dial code
40346             this.setFlagClass(this.dialCodeMapping[d].iso2);
40347             this.setDialCode(d);
40348             this.inputEl().dom.value = v.replace('+'+d,'');
40349             this.hiddenEl().dom.value = this.getValue();
40350             
40351             this.validate();
40352         },
40353         
40354         getDialCode : function(v)
40355         {
40356             v = v ||  '';
40357             
40358             if (v.length == 0) {
40359                 return this.dialCodeHolder.dom.value;
40360             }
40361             
40362             var dialCode = "";
40363             if (v.charAt(0) != "+") {
40364                 return false;
40365             }
40366             var numericChars = "";
40367             for (var i = 1; i < v.length; i++) {
40368               var c = v.charAt(i);
40369               if (!isNaN(c)) {
40370                 numericChars += c;
40371                 if (this.dialCodeMapping[numericChars]) {
40372                   dialCode = v.substr(1, i);
40373                 }
40374                 if (numericChars.length == 4) {
40375                   break;
40376                 }
40377               }
40378             }
40379             return dialCode;
40380         },
40381         
40382         reset : function()
40383         {
40384             this.setValue(this.defaultDialCode);
40385             this.validate();
40386         },
40387         
40388         hiddenEl : function()
40389         {
40390             return this.el.select('input.hidden-tel-input',true).first();
40391         },
40392         
40393         // after setting val
40394         onKeyUp : function(e){
40395             this.setValue(this.getValue());
40396         },
40397         
40398         onKeyPress : function(e){
40399             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40400                 e.stopEvent();
40401             }
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      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40479      */
40480     max_length: false,
40481     
40482     inputlg : 9,
40483     inputmd : 9,
40484     inputsm : 9,
40485     inputxs : 6,
40486     
40487     store : false,
40488     
40489     getAutoCreate : function()
40490     {
40491         var align = this.labelAlign || this.parentLabelAlign();
40492         
40493         var id = Roo.id();
40494
40495         var cfg = {
40496             cls: 'form-group',
40497             cn: []
40498         };
40499
40500         var input =  {
40501             tag: 'input',
40502             id : id,
40503             cls : 'form-control roo-money-amount-input',
40504             autocomplete: 'new-password'
40505         };
40506         
40507         var hiddenInput = {
40508             tag: 'input',
40509             type: 'hidden',
40510             id: Roo.id(),
40511             cls: 'hidden-number-input'
40512         };
40513         
40514         if(this.max_length) {
40515             input.maxlength = this.max_length; 
40516         }
40517         
40518         if (this.name) {
40519             hiddenInput.name = this.name;
40520         }
40521
40522         if (this.disabled) {
40523             input.disabled = true;
40524         }
40525
40526         var clg = 12 - this.inputlg;
40527         var cmd = 12 - this.inputmd;
40528         var csm = 12 - this.inputsm;
40529         var cxs = 12 - this.inputxs;
40530         
40531         var container = {
40532             tag : 'div',
40533             cls : 'row roo-money-field',
40534             cn : [
40535                 {
40536                     tag : 'div',
40537                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40538                     cn : [
40539                         {
40540                             tag : 'div',
40541                             cls: 'roo-select2-container input-group',
40542                             cn: [
40543                                 {
40544                                     tag : 'input',
40545                                     cls : 'form-control roo-money-currency-input',
40546                                     autocomplete: 'new-password',
40547                                     readOnly : 1,
40548                                     name : this.currencyName
40549                                 },
40550                                 {
40551                                     tag :'span',
40552                                     cls : 'input-group-addon',
40553                                     cn : [
40554                                         {
40555                                             tag: 'span',
40556                                             cls: 'caret'
40557                                         }
40558                                     ]
40559                                 }
40560                             ]
40561                         }
40562                     ]
40563                 },
40564                 {
40565                     tag : 'div',
40566                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40567                     cn : [
40568                         {
40569                             tag: 'div',
40570                             cls: this.hasFeedback ? 'has-feedback' : '',
40571                             cn: [
40572                                 input
40573                             ]
40574                         }
40575                     ]
40576                 }
40577             ]
40578             
40579         };
40580         
40581         if (this.fieldLabel.length) {
40582             var indicator = {
40583                 tag: 'i',
40584                 tooltip: 'This field is required'
40585             };
40586
40587             var label = {
40588                 tag: 'label',
40589                 'for':  id,
40590                 cls: 'control-label',
40591                 cn: []
40592             };
40593
40594             var label_text = {
40595                 tag: 'span',
40596                 html: this.fieldLabel
40597             };
40598
40599             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40600             label.cn = [
40601                 indicator,
40602                 label_text
40603             ];
40604
40605             if(this.indicatorpos == 'right') {
40606                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40607                 label.cn = [
40608                     label_text,
40609                     indicator
40610                 ];
40611             }
40612
40613             if(align == 'left') {
40614                 container = {
40615                     tag: 'div',
40616                     cn: [
40617                         container
40618                     ]
40619                 };
40620
40621                 if(this.labelWidth > 12){
40622                     label.style = "width: " + this.labelWidth + 'px';
40623                 }
40624                 if(this.labelWidth < 13 && this.labelmd == 0){
40625                     this.labelmd = this.labelWidth;
40626                 }
40627                 if(this.labellg > 0){
40628                     label.cls += ' col-lg-' + this.labellg;
40629                     input.cls += ' col-lg-' + (12 - this.labellg);
40630                 }
40631                 if(this.labelmd > 0){
40632                     label.cls += ' col-md-' + this.labelmd;
40633                     container.cls += ' col-md-' + (12 - this.labelmd);
40634                 }
40635                 if(this.labelsm > 0){
40636                     label.cls += ' col-sm-' + this.labelsm;
40637                     container.cls += ' col-sm-' + (12 - this.labelsm);
40638                 }
40639                 if(this.labelxs > 0){
40640                     label.cls += ' col-xs-' + this.labelxs;
40641                     container.cls += ' col-xs-' + (12 - this.labelxs);
40642                 }
40643             }
40644         }
40645
40646         cfg.cn = [
40647             label,
40648             container,
40649             hiddenInput
40650         ];
40651         
40652         var settings = this;
40653
40654         ['xs','sm','md','lg'].map(function(size){
40655             if (settings[size]) {
40656                 cfg.cls += ' col-' + size + '-' + settings[size];
40657             }
40658         });
40659         
40660         return cfg;
40661     },
40662     
40663     initEvents : function()
40664     {
40665         this.indicator = this.indicatorEl();
40666         
40667         this.initCurrencyEvent();
40668         
40669         this.initNumberEvent();
40670     },
40671     
40672     initCurrencyEvent : function()
40673     {
40674         if (!this.store) {
40675             throw "can not find store for combo";
40676         }
40677         
40678         this.store = Roo.factory(this.store, Roo.data);
40679         this.store.parent = this;
40680         
40681         this.createList();
40682         
40683         this.triggerEl = this.el.select('.input-group-addon', true).first();
40684         
40685         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40686         
40687         var _this = this;
40688         
40689         (function(){
40690             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40691             _this.list.setWidth(lw);
40692         }).defer(100);
40693         
40694         this.list.on('mouseover', this.onViewOver, this);
40695         this.list.on('mousemove', this.onViewMove, this);
40696         this.list.on('scroll', this.onViewScroll, this);
40697         
40698         if(!this.tpl){
40699             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40700         }
40701         
40702         this.view = new Roo.View(this.list, this.tpl, {
40703             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40704         });
40705         
40706         this.view.on('click', this.onViewClick, this);
40707         
40708         this.store.on('beforeload', this.onBeforeLoad, this);
40709         this.store.on('load', this.onLoad, this);
40710         this.store.on('loadexception', this.onLoadException, this);
40711         
40712         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40713             "up" : function(e){
40714                 this.inKeyMode = true;
40715                 this.selectPrev();
40716             },
40717
40718             "down" : function(e){
40719                 if(!this.isExpanded()){
40720                     this.onTriggerClick();
40721                 }else{
40722                     this.inKeyMode = true;
40723                     this.selectNext();
40724                 }
40725             },
40726
40727             "enter" : function(e){
40728                 this.collapse();
40729                 
40730                 if(this.fireEvent("specialkey", this, e)){
40731                     this.onViewClick(false);
40732                 }
40733                 
40734                 return true;
40735             },
40736
40737             "esc" : function(e){
40738                 this.collapse();
40739             },
40740
40741             "tab" : function(e){
40742                 this.collapse();
40743                 
40744                 if(this.fireEvent("specialkey", this, e)){
40745                     this.onViewClick(false);
40746                 }
40747                 
40748                 return true;
40749             },
40750
40751             scope : this,
40752
40753             doRelay : function(foo, bar, hname){
40754                 if(hname == 'down' || this.scope.isExpanded()){
40755                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40756                 }
40757                 return true;
40758             },
40759
40760             forceKeyDown: true
40761         });
40762         
40763         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40764         
40765     },
40766     
40767     initNumberEvent : function(e)
40768     {
40769         this.inputEl().on("keydown" , this.fireKey,  this);
40770         this.inputEl().on("focus", this.onFocus,  this);
40771         this.inputEl().on("blur", this.onBlur,  this);
40772         
40773         this.inputEl().relayEvent('keyup', this);
40774         
40775         if(this.indicator){
40776             this.indicator.addClass('invisible');
40777         }
40778  
40779         this.originalValue = this.getValue();
40780         
40781         if(this.validationEvent == 'keyup'){
40782             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40783             this.inputEl().on('keyup', this.filterValidation, this);
40784         }
40785         else if(this.validationEvent !== false){
40786             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40787         }
40788         
40789         if(this.selectOnFocus){
40790             this.on("focus", this.preFocus, this);
40791             
40792         }
40793         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40794             this.inputEl().on("keypress", this.filterKeys, this);
40795         } else {
40796             this.inputEl().relayEvent('keypress', this);
40797         }
40798         
40799         var allowed = "0123456789";
40800         
40801         if(this.allowDecimals){
40802             allowed += this.decimalSeparator;
40803         }
40804         
40805         if(this.allowNegative){
40806             allowed += "-";
40807         }
40808         
40809         if(this.thousandsDelimiter) {
40810             allowed += ",";
40811         }
40812         
40813         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40814         
40815         var keyPress = function(e){
40816             
40817             var k = e.getKey();
40818             
40819             var c = e.getCharCode();
40820             
40821             if(
40822                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40823                     allowed.indexOf(String.fromCharCode(c)) === -1
40824             ){
40825                 e.stopEvent();
40826                 return;
40827             }
40828             
40829             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40830                 return;
40831             }
40832             
40833             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40834                 e.stopEvent();
40835             }
40836         };
40837         
40838         this.inputEl().on("keypress", keyPress, this);
40839         
40840     },
40841     
40842     onTriggerClick : function(e)
40843     {   
40844         if(this.disabled){
40845             return;
40846         }
40847         
40848         this.page = 0;
40849         this.loadNext = false;
40850         
40851         if(this.isExpanded()){
40852             this.collapse();
40853             return;
40854         }
40855         
40856         this.hasFocus = true;
40857         
40858         if(this.triggerAction == 'all') {
40859             this.doQuery(this.allQuery, true);
40860             return;
40861         }
40862         
40863         this.doQuery(this.getRawValue());
40864     },
40865     
40866     getCurrency : function()
40867     {   
40868         var v = this.currencyEl().getValue();
40869         
40870         return v;
40871     },
40872     
40873     restrictHeight : function()
40874     {
40875         this.list.alignTo(this.currencyEl(), this.listAlign);
40876         this.list.alignTo(this.currencyEl(), this.listAlign);
40877     },
40878     
40879     onViewClick : function(view, doFocus, el, e)
40880     {
40881         var index = this.view.getSelectedIndexes()[0];
40882         
40883         var r = this.store.getAt(index);
40884         
40885         if(r){
40886             this.onSelect(r, index);
40887         }
40888     },
40889     
40890     onSelect : function(record, index){
40891         
40892         if(this.fireEvent('beforeselect', this, record, index) !== false){
40893         
40894             this.setFromCurrencyData(index > -1 ? record.data : false);
40895             
40896             this.collapse();
40897             
40898             this.fireEvent('select', this, record, index);
40899         }
40900     },
40901     
40902     setFromCurrencyData : function(o)
40903     {
40904         var currency = '';
40905         
40906         this.lastCurrency = o;
40907         
40908         if (this.currencyField) {
40909             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40910         } else {
40911             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40912         }
40913         
40914         this.lastSelectionText = currency;
40915         
40916         //setting default currency
40917         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40918             this.setCurrency(this.defaultCurrency);
40919             return;
40920         }
40921         
40922         this.setCurrency(currency);
40923     },
40924     
40925     setFromData : function(o)
40926     {
40927         var c = {};
40928         
40929         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40930         
40931         this.setFromCurrencyData(c);
40932         
40933         var value = '';
40934         
40935         if (this.name) {
40936             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40937         } else {
40938             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40939         }
40940         
40941         this.setValue(value);
40942         
40943     },
40944     
40945     setCurrency : function(v)
40946     {   
40947         this.currencyValue = v;
40948         
40949         if(this.rendered){
40950             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40951             this.validate();
40952         }
40953     },
40954     
40955     setValue : function(v)
40956     {
40957         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40958         
40959         this.value = v;
40960         
40961         if(this.rendered){
40962             
40963             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40964             
40965             this.inputEl().dom.value = (v == '') ? '' :
40966                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40967             
40968             if(!this.allowZero && v === '0') {
40969                 this.hiddenEl().dom.value = '';
40970                 this.inputEl().dom.value = '';
40971             }
40972             
40973             this.validate();
40974         }
40975     },
40976     
40977     getRawValue : function()
40978     {
40979         var v = this.inputEl().getValue();
40980         
40981         return v;
40982     },
40983     
40984     getValue : function()
40985     {
40986         return this.fixPrecision(this.parseValue(this.getRawValue()));
40987     },
40988     
40989     parseValue : function(value)
40990     {
40991         if(this.thousandsDelimiter) {
40992             value += "";
40993             r = new RegExp(",", "g");
40994             value = value.replace(r, "");
40995         }
40996         
40997         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40998         return isNaN(value) ? '' : value;
40999         
41000     },
41001     
41002     fixPrecision : function(value)
41003     {
41004         if(this.thousandsDelimiter) {
41005             value += "";
41006             r = new RegExp(",", "g");
41007             value = value.replace(r, "");
41008         }
41009         
41010         var nan = isNaN(value);
41011         
41012         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41013             return nan ? '' : value;
41014         }
41015         return parseFloat(value).toFixed(this.decimalPrecision);
41016     },
41017     
41018     decimalPrecisionFcn : function(v)
41019     {
41020         return Math.floor(v);
41021     },
41022     
41023     validateValue : function(value)
41024     {
41025         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41026             return false;
41027         }
41028         
41029         var num = this.parseValue(value);
41030         
41031         if(isNaN(num)){
41032             this.markInvalid(String.format(this.nanText, value));
41033             return false;
41034         }
41035         
41036         if(num < this.minValue){
41037             this.markInvalid(String.format(this.minText, this.minValue));
41038             return false;
41039         }
41040         
41041         if(num > this.maxValue){
41042             this.markInvalid(String.format(this.maxText, this.maxValue));
41043             return false;
41044         }
41045         
41046         return true;
41047     },
41048     
41049     validate : function()
41050     {
41051         if(this.disabled || this.allowBlank){
41052             this.markValid();
41053             return true;
41054         }
41055         
41056         var currency = this.getCurrency();
41057         
41058         if(this.validateValue(this.getRawValue()) && currency.length){
41059             this.markValid();
41060             return true;
41061         }
41062         
41063         this.markInvalid();
41064         return false;
41065     },
41066     
41067     getName: function()
41068     {
41069         return this.name;
41070     },
41071     
41072     beforeBlur : function()
41073     {
41074         if(!this.castInt){
41075             return;
41076         }
41077         
41078         var v = this.parseValue(this.getRawValue());
41079         
41080         if(v || v == 0){
41081             this.setValue(v);
41082         }
41083     },
41084     
41085     onBlur : function()
41086     {
41087         this.beforeBlur();
41088         
41089         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41090             //this.el.removeClass(this.focusClass);
41091         }
41092         
41093         this.hasFocus = false;
41094         
41095         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41096             this.validate();
41097         }
41098         
41099         var v = this.getValue();
41100         
41101         if(String(v) !== String(this.startValue)){
41102             this.fireEvent('change', this, v, this.startValue);
41103         }
41104         
41105         this.fireEvent("blur", this);
41106     },
41107     
41108     inputEl : function()
41109     {
41110         return this.el.select('.roo-money-amount-input', true).first();
41111     },
41112     
41113     currencyEl : function()
41114     {
41115         return this.el.select('.roo-money-currency-input', true).first();
41116     },
41117     
41118     hiddenEl : function()
41119     {
41120         return this.el.select('input.hidden-number-input',true).first();
41121     }
41122     
41123 });