Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
921     setWeight : function(str)
922     {
923         this.el.removeClass(this.weightClass);
924         this.el.addClass('btn-' + str);        
925     }
926     
927     
928 });
929
930  /*
931  * - LGPL
932  *
933  * column
934  * 
935  */
936
937 /**
938  * @class Roo.bootstrap.Column
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Column class
941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
949  *
950  * 
951  * @cfg {Boolean} hidden (true|false) hide the element
952  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953  * @cfg {String} fa (ban|check|...) font awesome icon
954  * @cfg {Number} fasize (1|2|....) font awsome size
955
956  * @cfg {String} icon (info-sign|check|...) glyphicon name
957
958  * @cfg {String} html content of column.
959  * 
960  * @constructor
961  * Create a new Column
962  * @param {Object} config The config object
963  */
964
965 Roo.bootstrap.Column = function(config){
966     Roo.bootstrap.Column.superclass.constructor.call(this, config);
967 };
968
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
970     
971     xs: false,
972     sm: false,
973     md: false,
974     lg: false,
975     xsoff: false,
976     smoff: false,
977     mdoff: false,
978     lgoff: false,
979     html: '',
980     offset: 0,
981     alert: false,
982     fa: false,
983     icon : false,
984     hidden : false,
985     fasize : 1,
986     
987     getAutoCreate : function(){
988         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
989         
990         cfg = {
991             tag: 'div',
992             cls: 'column'
993         };
994         
995         var settings=this;
996         ['xs','sm','md','lg'].map(function(size){
997             //Roo.log( size + ':' + settings[size]);
998             
999             if (settings[size+'off'] !== false) {
1000                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1001             }
1002             
1003             if (settings[size] === false) {
1004                 return;
1005             }
1006             
1007             if (!settings[size]) { // 0 = hidden
1008                 cfg.cls += ' hidden-' + size;
1009                 return;
1010             }
1011             cfg.cls += ' col-' + size + '-' + settings[size];
1012             
1013         });
1014         
1015         if (this.hidden) {
1016             cfg.cls += ' hidden';
1017         }
1018         
1019         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020             cfg.cls +=' alert alert-' + this.alert;
1021         }
1022         
1023         
1024         if (this.html.length) {
1025             cfg.html = this.html;
1026         }
1027         if (this.fa) {
1028             var fasize = '';
1029             if (this.fasize > 1) {
1030                 fasize = ' fa-' + this.fasize + 'x';
1031             }
1032             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1033             
1034             
1035         }
1036         if (this.icon) {
1037             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1038         }
1039         
1040         return cfg;
1041     }
1042    
1043 });
1044
1045  
1046
1047  /*
1048  * - LGPL
1049  *
1050  * page container.
1051  * 
1052  */
1053
1054
1055 /**
1056  * @class Roo.bootstrap.Container
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Container class
1059  * @cfg {Boolean} jumbotron is it a jumbotron element
1060  * @cfg {String} html content of element
1061  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1063  * @cfg {String} header content of header (for panel)
1064  * @cfg {String} footer content of footer (for panel)
1065  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066  * @cfg {String} tag (header|aside|section) type of HTML tag.
1067  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068  * @cfg {String} fa font awesome icon
1069  * @cfg {String} icon (info-sign|check|...) glyphicon name
1070  * @cfg {Boolean} hidden (true|false) hide the element
1071  * @cfg {Boolean} expandable (true|false) default false
1072  * @cfg {Boolean} expanded (true|false) default true
1073  * @cfg {String} rheader contet on the right of header
1074  * @cfg {Boolean} clickable (true|false) default false
1075
1076  *     
1077  * @constructor
1078  * Create a new Container
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Container = function(config){
1083     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // raw events
1087          /**
1088          * @event expand
1089          * After the panel has been expand
1090          * 
1091          * @param {Roo.bootstrap.Container} this
1092          */
1093         "expand" : true,
1094         /**
1095          * @event collapse
1096          * After the panel has been collapsed
1097          * 
1098          * @param {Roo.bootstrap.Container} this
1099          */
1100         "collapse" : true,
1101         /**
1102          * @event click
1103          * When a element is chick
1104          * @param {Roo.bootstrap.Container} this
1105          * @param {Roo.EventObject} e
1106          */
1107         "click" : true
1108     });
1109 };
1110
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1112     
1113     jumbotron : false,
1114     well: '',
1115     panel : '',
1116     header: '',
1117     footer : '',
1118     sticky: '',
1119     tag : false,
1120     alert : false,
1121     fa: false,
1122     icon : false,
1123     expandable : false,
1124     rheader : '',
1125     expanded : true,
1126     clickable: false,
1127   
1128      
1129     getChildContainer : function() {
1130         
1131         if(!this.el){
1132             return false;
1133         }
1134         
1135         if (this.panel.length) {
1136             return this.el.select('.panel-body',true).first();
1137         }
1138         
1139         return this.el;
1140     },
1141     
1142     
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag : this.tag || 'div',
1147             html : '',
1148             cls : ''
1149         };
1150         if (this.jumbotron) {
1151             cfg.cls = 'jumbotron';
1152         }
1153         
1154         
1155         
1156         // - this is applied by the parent..
1157         //if (this.cls) {
1158         //    cfg.cls = this.cls + '';
1159         //}
1160         
1161         if (this.sticky.length) {
1162             
1163             var bd = Roo.get(document.body);
1164             if (!bd.hasClass('bootstrap-sticky')) {
1165                 bd.addClass('bootstrap-sticky');
1166                 Roo.select('html',true).setStyle('height', '100%');
1167             }
1168              
1169             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1170         }
1171         
1172         
1173         if (this.well.length) {
1174             switch (this.well) {
1175                 case 'lg':
1176                 case 'sm':
1177                     cfg.cls +=' well well-' +this.well;
1178                     break;
1179                 default:
1180                     cfg.cls +=' well';
1181                     break;
1182             }
1183         }
1184         
1185         if (this.hidden) {
1186             cfg.cls += ' hidden';
1187         }
1188         
1189         
1190         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191             cfg.cls +=' alert alert-' + this.alert;
1192         }
1193         
1194         var body = cfg;
1195         
1196         if (this.panel.length) {
1197             cfg.cls += ' panel panel-' + this.panel;
1198             cfg.cn = [];
1199             if (this.header.length) {
1200                 
1201                 var h = [];
1202                 
1203                 if(this.expandable){
1204                     
1205                     cfg.cls = cfg.cls + ' expandable';
1206                     
1207                     h.push({
1208                         tag: 'i',
1209                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1210                     });
1211                     
1212                 }
1213                 
1214                 h.push(
1215                     {
1216                         tag: 'span',
1217                         cls : 'panel-title',
1218                         html : (this.expandable ? '&nbsp;' : '') + this.header
1219                     },
1220                     {
1221                         tag: 'span',
1222                         cls: 'panel-header-right',
1223                         html: this.rheader
1224                     }
1225                 );
1226                 
1227                 cfg.cn.push({
1228                     cls : 'panel-heading',
1229                     style : this.expandable ? 'cursor: pointer' : '',
1230                     cn : h
1231                 });
1232                 
1233             }
1234             
1235             body = false;
1236             cfg.cn.push({
1237                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1238                 html : this.html
1239             });
1240             
1241             
1242             if (this.footer.length) {
1243                 cfg.cn.push({
1244                     cls : 'panel-footer',
1245                     html : this.footer
1246                     
1247                 });
1248             }
1249             
1250         }
1251         
1252         if (body) {
1253             body.html = this.html || cfg.html;
1254             // prefix with the icons..
1255             if (this.fa) {
1256                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1257             }
1258             if (this.icon) {
1259                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1260             }
1261             
1262             
1263         }
1264         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265             cfg.cls =  'container';
1266         }
1267         
1268         return cfg;
1269     },
1270     
1271     initEvents: function() 
1272     {
1273         if(this.expandable){
1274             var headerEl = this.headerEl();
1275         
1276             if(headerEl){
1277                 headerEl.on('click', this.onToggleClick, this);
1278             }
1279         }
1280         
1281         if(this.clickable){
1282             this.el.on('click', this.onClick, this);
1283         }
1284         
1285     },
1286     
1287     onToggleClick : function()
1288     {
1289         var headerEl = this.headerEl();
1290         
1291         if(!headerEl){
1292             return;
1293         }
1294         
1295         if(this.expanded){
1296             this.collapse();
1297             return;
1298         }
1299         
1300         this.expand();
1301     },
1302     
1303     expand : function()
1304     {
1305         if(this.fireEvent('expand', this)) {
1306             
1307             this.expanded = true;
1308             
1309             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1310             
1311             this.el.select('.panel-body',true).first().removeClass('hide');
1312             
1313             var toggleEl = this.toggleEl();
1314
1315             if(!toggleEl){
1316                 return;
1317             }
1318
1319             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1320         }
1321         
1322     },
1323     
1324     collapse : function()
1325     {
1326         if(this.fireEvent('collapse', this)) {
1327             
1328             this.expanded = false;
1329             
1330             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331             this.el.select('.panel-body',true).first().addClass('hide');
1332         
1333             var toggleEl = this.toggleEl();
1334
1335             if(!toggleEl){
1336                 return;
1337             }
1338
1339             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1340         }
1341     },
1342     
1343     toggleEl : function()
1344     {
1345         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1346             return;
1347         }
1348         
1349         return this.el.select('.panel-heading .fa',true).first();
1350     },
1351     
1352     headerEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading',true).first()
1359     },
1360     
1361     bodyEl : function()
1362     {
1363         if(!this.el || !this.panel.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-body',true).first()
1368     },
1369     
1370     titleEl : function()
1371     {
1372         if(!this.el || !this.panel.length || !this.header.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-title',true).first();
1377     },
1378     
1379     setTitle : function(v)
1380     {
1381         var titleEl = this.titleEl();
1382         
1383         if(!titleEl){
1384             return;
1385         }
1386         
1387         titleEl.dom.innerHTML = v;
1388     },
1389     
1390     getTitle : function()
1391     {
1392         
1393         var titleEl = this.titleEl();
1394         
1395         if(!titleEl){
1396             return '';
1397         }
1398         
1399         return titleEl.dom.innerHTML;
1400     },
1401     
1402     setRightTitle : function(v)
1403     {
1404         var t = this.el.select('.panel-header-right',true).first();
1405         
1406         if(!t){
1407             return;
1408         }
1409         
1410         t.dom.innerHTML = v;
1411     },
1412     
1413     onClick : function(e)
1414     {
1415         e.preventDefault();
1416         
1417         this.fireEvent('click', this, e);
1418     }
1419 });
1420
1421  /*
1422  * - LGPL
1423  *
1424  * image
1425  * 
1426  */
1427
1428
1429 /**
1430  * @class Roo.bootstrap.Img
1431  * @extends Roo.bootstrap.Component
1432  * Bootstrap Img class
1433  * @cfg {Boolean} imgResponsive false | true
1434  * @cfg {String} border rounded | circle | thumbnail
1435  * @cfg {String} src image source
1436  * @cfg {String} alt image alternative text
1437  * @cfg {String} href a tag href
1438  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439  * @cfg {String} xsUrl xs image source
1440  * @cfg {String} smUrl sm image source
1441  * @cfg {String} mdUrl md image source
1442  * @cfg {String} lgUrl lg image source
1443  * 
1444  * @constructor
1445  * Create a new Input
1446  * @param {Object} config The config object
1447  */
1448
1449 Roo.bootstrap.Img = function(config){
1450     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451     
1452     this.addEvents({
1453         // img events
1454         /**
1455          * @event click
1456          * The img click event for the img.
1457          * @param {Roo.EventObject} e
1458          */
1459         "click" : true
1460     });
1461 };
1462
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1464     
1465     imgResponsive: true,
1466     border: '',
1467     src: 'about:blank',
1468     href: false,
1469     target: false,
1470     xsUrl: '',
1471     smUrl: '',
1472     mdUrl: '',
1473     lgUrl: '',
1474
1475     getAutoCreate : function()
1476     {   
1477         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478             return this.createSingleImg();
1479         }
1480         
1481         var cfg = {
1482             tag: 'div',
1483             cls: 'roo-image-responsive-group',
1484             cn: []
1485         };
1486         var _this = this;
1487         
1488         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1489             
1490             if(!_this[size + 'Url']){
1491                 return;
1492             }
1493             
1494             var img = {
1495                 tag: 'img',
1496                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497                 html: _this.html || cfg.html,
1498                 src: _this[size + 'Url']
1499             };
1500             
1501             img.cls += ' roo-image-responsive-' + size;
1502             
1503             var s = ['xs', 'sm', 'md', 'lg'];
1504             
1505             s.splice(s.indexOf(size), 1);
1506             
1507             Roo.each(s, function(ss){
1508                 img.cls += ' hidden-' + ss;
1509             });
1510             
1511             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512                 cfg.cls += ' img-' + _this.border;
1513             }
1514             
1515             if(_this.alt){
1516                 cfg.alt = _this.alt;
1517             }
1518             
1519             if(_this.href){
1520                 var a = {
1521                     tag: 'a',
1522                     href: _this.href,
1523                     cn: [
1524                         img
1525                     ]
1526                 };
1527
1528                 if(this.target){
1529                     a.target = _this.target;
1530                 }
1531             }
1532             
1533             cfg.cn.push((_this.href) ? a : img);
1534             
1535         });
1536         
1537         return cfg;
1538     },
1539     
1540     createSingleImg : function()
1541     {
1542         var cfg = {
1543             tag: 'img',
1544             cls: (this.imgResponsive) ? 'img-responsive' : '',
1545             html : null,
1546             src : 'about:blank'  // just incase src get's set to undefined?!?
1547         };
1548         
1549         cfg.html = this.html || cfg.html;
1550         
1551         cfg.src = this.src || cfg.src;
1552         
1553         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554             cfg.cls += ' img-' + this.border;
1555         }
1556         
1557         if(this.alt){
1558             cfg.alt = this.alt;
1559         }
1560         
1561         if(this.href){
1562             var a = {
1563                 tag: 'a',
1564                 href: this.href,
1565                 cn: [
1566                     cfg
1567                 ]
1568             };
1569             
1570             if(this.target){
1571                 a.target = this.target;
1572             }
1573             
1574         }
1575         
1576         return (this.href) ? a : cfg;
1577     },
1578     
1579     initEvents: function() 
1580     {
1581         if(!this.href){
1582             this.el.on('click', this.onClick, this);
1583         }
1584         
1585     },
1586     
1587     onClick : function(e)
1588     {
1589         Roo.log('img onclick');
1590         this.fireEvent('click', this, e);
1591     },
1592     /**
1593      * Sets the url of the image - used to update it
1594      * @param {String} url the url of the image
1595      */
1596     
1597     setSrc : function(url)
1598     {
1599         this.src =  url;
1600         
1601         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602             this.el.dom.src =  url;
1603             return;
1604         }
1605         
1606         this.el.select('img', true).first().dom.src =  url;
1607     }
1608     
1609     
1610    
1611 });
1612
1613  /*
1614  * - LGPL
1615  *
1616  * image
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.Link
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap Link Class
1625  * @cfg {String} alt image alternative text
1626  * @cfg {String} href a tag href
1627  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628  * @cfg {String} html the content of the link.
1629  * @cfg {String} anchor name for the anchor link
1630  * @cfg {String} fa - favicon
1631
1632  * @cfg {Boolean} preventDefault (true | false) default false
1633
1634  * 
1635  * @constructor
1636  * Create a new Input
1637  * @param {Object} config The config object
1638  */
1639
1640 Roo.bootstrap.Link = function(config){
1641     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642     
1643     this.addEvents({
1644         // img events
1645         /**
1646          * @event click
1647          * The img click event for the img.
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1655     
1656     href: false,
1657     target: false,
1658     preventDefault: false,
1659     anchor : false,
1660     alt : false,
1661     fa: false,
1662
1663
1664     getAutoCreate : function()
1665     {
1666         var html = this.html || '';
1667         
1668         if (this.fa !== false) {
1669             html = '<i class="fa fa-' + this.fa + '"></i>';
1670         }
1671         var cfg = {
1672             tag: 'a'
1673         };
1674         // anchor's do not require html/href...
1675         if (this.anchor === false) {
1676             cfg.html = html;
1677             cfg.href = this.href || '#';
1678         } else {
1679             cfg.name = this.anchor;
1680             if (this.html !== false || this.fa !== false) {
1681                 cfg.html = html;
1682             }
1683             if (this.href !== false) {
1684                 cfg.href = this.href;
1685             }
1686         }
1687         
1688         if(this.alt !== false){
1689             cfg.alt = this.alt;
1690         }
1691         
1692         
1693         if(this.target !== false) {
1694             cfg.target = this.target;
1695         }
1696         
1697         return cfg;
1698     },
1699     
1700     initEvents: function() {
1701         
1702         if(!this.href || this.preventDefault){
1703             this.el.on('click', this.onClick, this);
1704         }
1705     },
1706     
1707     onClick : function(e)
1708     {
1709         if(this.preventDefault){
1710             e.preventDefault();
1711         }
1712         //Roo.log('img onclick');
1713         this.fireEvent('click', this, e);
1714     }
1715    
1716 });
1717
1718  /*
1719  * - LGPL
1720  *
1721  * header
1722  * 
1723  */
1724
1725 /**
1726  * @class Roo.bootstrap.Header
1727  * @extends Roo.bootstrap.Component
1728  * Bootstrap Header class
1729  * @cfg {String} html content of header
1730  * @cfg {Number} level (1|2|3|4|5|6) default 1
1731  * 
1732  * @constructor
1733  * Create a new Header
1734  * @param {Object} config The config object
1735  */
1736
1737
1738 Roo.bootstrap.Header  = function(config){
1739     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1740 };
1741
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1743     
1744     //href : false,
1745     html : false,
1746     level : 1,
1747     
1748     
1749     
1750     getAutoCreate : function(){
1751         
1752         
1753         
1754         var cfg = {
1755             tag: 'h' + (1 *this.level),
1756             html: this.html || ''
1757         } ;
1758         
1759         return cfg;
1760     }
1761    
1762 });
1763
1764  
1765
1766  /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776  
1777 /**
1778  * @class Roo.bootstrap.MenuMgr
1779  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1780  * @singleton
1781  */
1782 Roo.bootstrap.MenuMgr = function(){
1783    var menus, active, groups = {}, attached = false, lastShow = new Date();
1784
1785    // private - called when first menu is created
1786    function init(){
1787        menus = {};
1788        active = new Roo.util.MixedCollection();
1789        Roo.get(document).addKeyListener(27, function(){
1790            if(active.length > 0){
1791                hideAll();
1792            }
1793        });
1794    }
1795
1796    // private
1797    function hideAll(){
1798        if(active && active.length > 0){
1799            var c = active.clone();
1800            c.each(function(m){
1801                m.hide();
1802            });
1803        }
1804    }
1805
1806    // private
1807    function onHide(m){
1808        active.remove(m);
1809        if(active.length < 1){
1810            Roo.get(document).un("mouseup", onMouseDown);
1811             
1812            attached = false;
1813        }
1814    }
1815
1816    // private
1817    function onShow(m){
1818        var last = active.last();
1819        lastShow = new Date();
1820        active.add(m);
1821        if(!attached){
1822           Roo.get(document).on("mouseup", onMouseDown);
1823            
1824            attached = true;
1825        }
1826        if(m.parentMenu){
1827           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828           m.parentMenu.activeChild = m;
1829        }else if(last && last.isVisible()){
1830           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1831        }
1832    }
1833
1834    // private
1835    function onBeforeHide(m){
1836        if(m.activeChild){
1837            m.activeChild.hide();
1838        }
1839        if(m.autoHideTimer){
1840            clearTimeout(m.autoHideTimer);
1841            delete m.autoHideTimer;
1842        }
1843    }
1844
1845    // private
1846    function onBeforeShow(m){
1847        var pm = m.parentMenu;
1848        if(!pm && !m.allowOtherMenus){
1849            hideAll();
1850        }else if(pm && pm.activeChild && active != m){
1851            pm.activeChild.hide();
1852        }
1853    }
1854
1855    // private this should really trigger on mouseup..
1856    function onMouseDown(e){
1857         Roo.log("on Mouse Up");
1858         
1859         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860             Roo.log("MenuManager hideAll");
1861             hideAll();
1862             e.stopEvent();
1863         }
1864         
1865         
1866    }
1867
1868    // private
1869    function onBeforeCheck(mi, state){
1870        if(state){
1871            var g = groups[mi.group];
1872            for(var i = 0, l = g.length; i < l; i++){
1873                if(g[i] != mi){
1874                    g[i].setChecked(false);
1875                }
1876            }
1877        }
1878    }
1879
1880    return {
1881
1882        /**
1883         * Hides all menus that are currently visible
1884         */
1885        hideAll : function(){
1886             hideAll();  
1887        },
1888
1889        // private
1890        register : function(menu){
1891            if(!menus){
1892                init();
1893            }
1894            menus[menu.id] = menu;
1895            menu.on("beforehide", onBeforeHide);
1896            menu.on("hide", onHide);
1897            menu.on("beforeshow", onBeforeShow);
1898            menu.on("show", onShow);
1899            var g = menu.group;
1900            if(g && menu.events["checkchange"]){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menu);
1905                menu.on("checkchange", onCheck);
1906            }
1907        },
1908
1909         /**
1910          * Returns a {@link Roo.menu.Menu} object
1911          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912          * be used to generate and return a new Menu instance.
1913          */
1914        get : function(menu){
1915            if(typeof menu == "string"){ // menu id
1916                return menus[menu];
1917            }else if(menu.events){  // menu instance
1918                return menu;
1919            }
1920            /*else if(typeof menu.length == 'number'){ // array of menu items?
1921                return new Roo.bootstrap.Menu({items:menu});
1922            }else{ // otherwise, must be a config
1923                return new Roo.bootstrap.Menu(menu);
1924            }
1925            */
1926            return false;
1927        },
1928
1929        // private
1930        unregister : function(menu){
1931            delete menus[menu.id];
1932            menu.un("beforehide", onBeforeHide);
1933            menu.un("hide", onHide);
1934            menu.un("beforeshow", onBeforeShow);
1935            menu.un("show", onShow);
1936            var g = menu.group;
1937            if(g && menu.events["checkchange"]){
1938                groups[g].remove(menu);
1939                menu.un("checkchange", onCheck);
1940            }
1941        },
1942
1943        // private
1944        registerCheckable : function(menuItem){
1945            var g = menuItem.group;
1946            if(g){
1947                if(!groups[g]){
1948                    groups[g] = [];
1949                }
1950                groups[g].push(menuItem);
1951                menuItem.on("beforecheckchange", onBeforeCheck);
1952            }
1953        },
1954
1955        // private
1956        unregisterCheckable : function(menuItem){
1957            var g = menuItem.group;
1958            if(g){
1959                groups[g].remove(menuItem);
1960                menuItem.un("beforecheckchange", onBeforeCheck);
1961            }
1962        }
1963    };
1964 }();/*
1965  * - LGPL
1966  *
1967  * menu
1968  * 
1969  */
1970
1971 /**
1972  * @class Roo.bootstrap.Menu
1973  * @extends Roo.bootstrap.Component
1974  * Bootstrap Menu class - container for MenuItems
1975  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1977  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1978  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1979  * 
1980  * @constructor
1981  * Create a new Menu
1982  * @param {Object} config The config object
1983  */
1984
1985
1986 Roo.bootstrap.Menu = function(config){
1987     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988     if (this.registerMenu && this.type != 'treeview')  {
1989         Roo.bootstrap.MenuMgr.register(this);
1990     }
1991     this.addEvents({
1992         /**
1993          * @event beforeshow
1994          * Fires before this menu is displayed
1995          * @param {Roo.menu.Menu} this
1996          */
1997         beforeshow : true,
1998         /**
1999          * @event beforehide
2000          * Fires before this menu is hidden
2001          * @param {Roo.menu.Menu} this
2002          */
2003         beforehide : true,
2004         /**
2005          * @event show
2006          * Fires after this menu is displayed
2007          * @param {Roo.menu.Menu} this
2008          */
2009         show : true,
2010         /**
2011          * @event hide
2012          * Fires after this menu is hidden
2013          * @param {Roo.menu.Menu} this
2014          */
2015         hide : true,
2016         /**
2017          * @event click
2018          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021          * @param {Roo.EventObject} e
2022          */
2023         click : true,
2024         /**
2025          * @event mouseover
2026          * Fires when the mouse is hovering over this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseover : true,
2032         /**
2033          * @event mouseout
2034          * Fires when the mouse exits this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseout : true,
2040         /**
2041          * @event itemclick
2042          * Fires when a menu item contained in this menu is clicked
2043          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044          * @param {Roo.EventObject} e
2045          */
2046         itemclick: true
2047     });
2048     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2049 };
2050
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2052     
2053    /// html : false,
2054     //align : '',
2055     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2056     type: false,
2057     /**
2058      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2059      */
2060     registerMenu : true,
2061     
2062     menuItems :false, // stores the menu items..
2063     
2064     hidden:true,
2065         
2066     parentMenu : false,
2067     
2068     stopEvent : true,
2069     
2070     isLink : false,
2071     
2072     getChildContainer : function() {
2073         return this.el;  
2074     },
2075     
2076     getAutoCreate : function(){
2077          
2078         //if (['right'].indexOf(this.align)!==-1) {
2079         //    cfg.cn[1].cls += ' pull-right'
2080         //}
2081         
2082         
2083         var cfg = {
2084             tag : 'ul',
2085             cls : 'dropdown-menu' ,
2086             style : 'z-index:1000'
2087             
2088         };
2089         
2090         if (this.type === 'submenu') {
2091             cfg.cls = 'submenu active';
2092         }
2093         if (this.type === 'treeview') {
2094             cfg.cls = 'treeview-menu';
2095         }
2096         
2097         return cfg;
2098     },
2099     initEvents : function() {
2100         
2101        // Roo.log("ADD event");
2102        // Roo.log(this.triggerEl.dom);
2103         
2104         this.triggerEl.on('click', this.onTriggerClick, this);
2105         
2106         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2107         
2108         this.triggerEl.addClass('dropdown-toggle');
2109         
2110         if (Roo.isTouch) {
2111             this.el.on('touchstart'  , this.onTouch, this);
2112         }
2113         this.el.on('click' , this.onClick, this);
2114
2115         this.el.on("mouseover", this.onMouseOver, this);
2116         this.el.on("mouseout", this.onMouseOut, this);
2117         
2118     },
2119     
2120     findTargetItem : function(e)
2121     {
2122         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2123         if(!t){
2124             return false;
2125         }
2126         //Roo.log(t);         Roo.log(t.id);
2127         if(t && t.id){
2128             //Roo.log(this.menuitems);
2129             return this.menuitems.get(t.id);
2130             
2131             //return this.items.get(t.menuItemId);
2132         }
2133         
2134         return false;
2135     },
2136     
2137     onTouch : function(e) 
2138     {
2139         Roo.log("menu.onTouch");
2140         //e.stopEvent(); this make the user popdown broken
2141         this.onClick(e);
2142     },
2143     
2144     onClick : function(e)
2145     {
2146         Roo.log("menu.onClick");
2147         
2148         var t = this.findTargetItem(e);
2149         if(!t || t.isContainer){
2150             return;
2151         }
2152         Roo.log(e);
2153         /*
2154         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2155             if(t == this.activeItem && t.shouldDeactivate(e)){
2156                 this.activeItem.deactivate();
2157                 delete this.activeItem;
2158                 return;
2159             }
2160             if(t.canActivate){
2161                 this.setActiveItem(t, true);
2162             }
2163             return;
2164             
2165             
2166         }
2167         */
2168        
2169         Roo.log('pass click event');
2170         
2171         t.onClick(e);
2172         
2173         this.fireEvent("click", this, t, e);
2174         
2175         var _this = this;
2176         
2177         if(!t.href.length || t.href == '#'){
2178             (function() { _this.hide(); }).defer(100);
2179         }
2180         
2181     },
2182     
2183     onMouseOver : function(e){
2184         var t  = this.findTargetItem(e);
2185         //Roo.log(t);
2186         //if(t){
2187         //    if(t.canActivate && !t.disabled){
2188         //        this.setActiveItem(t, true);
2189         //    }
2190         //}
2191         
2192         this.fireEvent("mouseover", this, e, t);
2193     },
2194     isVisible : function(){
2195         return !this.hidden;
2196     },
2197      onMouseOut : function(e){
2198         var t  = this.findTargetItem(e);
2199         
2200         //if(t ){
2201         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2202         //        this.activeItem.deactivate();
2203         //        delete this.activeItem;
2204         //    }
2205         //}
2206         this.fireEvent("mouseout", this, e, t);
2207     },
2208     
2209     
2210     /**
2211      * Displays this menu relative to another element
2212      * @param {String/HTMLElement/Roo.Element} element The element to align to
2213      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214      * the element (defaults to this.defaultAlign)
2215      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2216      */
2217     show : function(el, pos, parentMenu){
2218         this.parentMenu = parentMenu;
2219         if(!this.el){
2220             this.render();
2221         }
2222         this.fireEvent("beforeshow", this);
2223         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2224     },
2225      /**
2226      * Displays this menu at a specific xy position
2227      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     showAt : function(xy, parentMenu, /* private: */_e){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         if(_e !== false){
2236             this.fireEvent("beforeshow", this);
2237             //xy = this.el.adjustForConstraints(xy);
2238         }
2239         
2240         //this.el.show();
2241         this.hideMenuItems();
2242         this.hidden = false;
2243         this.triggerEl.addClass('open');
2244         
2245         // reassign x when hitting right
2246         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2248         }
2249         
2250         // reassign y when hitting bottom
2251         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2253         }
2254         
2255         // but the list may align on trigger left or trigger top... should it be a properity?
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  * @cfg {Number} max_width set the max width of modal
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628     
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637     
2638     max_width: 0,
2639     
2640     max_height: 0,
2641     
2642     fit_content: false,
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         
2681         //this.el.addClass("x-dlg-modal");
2682
2683         if (this.buttons.length) {
2684             Roo.each(this.buttons, function(bb) {
2685                 var b = Roo.apply({}, bb);
2686                 b.xns = b.xns || Roo.bootstrap;
2687                 b.xtype = b.xtype || 'Button';
2688                 if (typeof(b.listeners) == 'undefined') {
2689                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2690                 }
2691
2692                 var btn = Roo.factory(b);
2693
2694                 btn.render(this.el.select('.modal-footer div').first());
2695
2696             },this);
2697         }
2698         // render the children.
2699         var nitems = [];
2700
2701         if(typeof(this.items) != 'undefined'){
2702             var items = this.items;
2703             delete this.items;
2704
2705             for(var i =0;i < items.length;i++) {
2706                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2707             }
2708         }
2709
2710         this.items = nitems;
2711
2712         // where are these used - they used to be body/close/footer
2713
2714
2715         this.initEvents();
2716         //this.el.addClass([this.fieldClass, this.cls]);
2717
2718     },
2719
2720     getAutoCreate : function()
2721     {
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(
2819             Roo.lib.Dom.getViewWidth(true),
2820             Roo.lib.Dom.getViewHeight(true)
2821         );
2822         
2823         if (this.fitwindow) {
2824             this.setSize(
2825                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2827             );
2828             return;
2829         }
2830         
2831         if(this.max_width !== 0) {
2832             
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             if(this.height) {
2836                 this.setSize(w, this.height);
2837                 return;
2838             }
2839             
2840             if(this.max_height) {
2841                 this.setSize(w,Math.min(
2842                     this.max_height,
2843                     Roo.lib.Dom.getViewportHeight(true) - 60
2844                 ));
2845                 
2846                 return;
2847             }
2848             
2849             if(!this.fit_content) {
2850                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2851                 return;
2852             }
2853             
2854             this.setSize(w, Math.min(
2855                 60 +
2856                 this.headerEl.getHeight() + 
2857                 this.footerEl.getHeight() + 
2858                 this.getChildHeight(this.bodyEl.dom.childNodes),
2859                 Roo.lib.Dom.getViewportHeight(true) - 60)
2860             );
2861         }
2862         
2863     },
2864
2865     setSize : function(w,h)
2866     {
2867         if (!w && !h) {
2868             return;
2869         }
2870         
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h - this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     },
3024     
3025     getChildHeight : function(child_nodes)
3026     {
3027         if(
3028             !child_nodes ||
3029             child_nodes.length == 0
3030         ) {
3031             return;
3032         }
3033         
3034         var child_height = 0;
3035         
3036         for(var i = 0; i < child_nodes.length; i++) {
3037             
3038             /*
3039             * for modal with tabs...
3040             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3041                 
3042                 var layout_childs = child_nodes[i].childNodes;
3043                 
3044                 for(var j = 0; j < layout_childs.length; j++) {
3045                     
3046                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3047                         
3048                         var layout_body_childs = layout_childs[j].childNodes;
3049                         
3050                         for(var k = 0; k < layout_body_childs.length; k++) {
3051                             
3052                             if(layout_body_childs[k].classList.contains('navbar')) {
3053                                 child_height += layout_body_childs[k].offsetHeight;
3054                                 continue;
3055                             }
3056                             
3057                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3058                                 
3059                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3060                                 
3061                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3062                                     
3063                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3065                                         continue;
3066                                     }
3067                                     
3068                                 }
3069                                 
3070                             }
3071                             
3072                         }
3073                     }
3074                 }
3075                 continue;
3076             }
3077             */
3078             
3079             child_height += child_nodes[i].offsetHeight;
3080             // Roo.log(child_nodes[i].offsetHeight);
3081         }
3082         
3083         return child_height;
3084     }
3085
3086 });
3087
3088
3089 Roo.apply(Roo.bootstrap.Modal,  {
3090     /**
3091          * Button config that displays a single OK button
3092          * @type Object
3093          */
3094         OK :  [{
3095             name : 'ok',
3096             weight : 'primary',
3097             html : 'OK'
3098         }],
3099         /**
3100          * Button config that displays Yes and No buttons
3101          * @type Object
3102          */
3103         YESNO : [
3104             {
3105                 name  : 'no',
3106                 html : 'No'
3107             },
3108             {
3109                 name  :'yes',
3110                 weight : 'primary',
3111                 html : 'Yes'
3112             }
3113         ],
3114
3115         /**
3116          * Button config that displays OK and Cancel buttons
3117          * @type Object
3118          */
3119         OKCANCEL : [
3120             {
3121                name : 'cancel',
3122                 html : 'Cancel'
3123             },
3124             {
3125                 name : 'ok',
3126                 weight : 'primary',
3127                 html : 'OK'
3128             }
3129         ],
3130         /**
3131          * Button config that displays Yes, No and Cancel buttons
3132          * @type Object
3133          */
3134         YESNOCANCEL : [
3135             {
3136                 name : 'yes',
3137                 weight : 'primary',
3138                 html : 'Yes'
3139             },
3140             {
3141                 name : 'no',
3142                 html : 'No'
3143             },
3144             {
3145                 name : 'cancel',
3146                 html : 'Cancel'
3147             }
3148         ],
3149         
3150         zIndex : 10001
3151 });
3152 /*
3153  * - LGPL
3154  *
3155  * messagebox - can be used as a replace
3156  * 
3157  */
3158 /**
3159  * @class Roo.MessageBox
3160  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3161  * Example usage:
3162  *<pre><code>
3163 // Basic alert:
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3165
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3168     if (btn == 'ok'){
3169         // process text value...
3170     }
3171 });
3172
3173 // Show a dialog using config options:
3174 Roo.Msg.show({
3175    title:'Save Changes?',
3176    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177    buttons: Roo.Msg.YESNOCANCEL,
3178    fn: processResult,
3179    animEl: 'elId'
3180 });
3181 </code></pre>
3182  * @singleton
3183  */
3184 Roo.bootstrap.MessageBox = function(){
3185     var dlg, opt, mask, waitTimer;
3186     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187     var buttons, activeTextEl, bwidth;
3188
3189     
3190     // private
3191     var handleButton = function(button){
3192         dlg.hide();
3193         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3194     };
3195
3196     // private
3197     var handleHide = function(){
3198         if(opt && opt.cls){
3199             dlg.el.removeClass(opt.cls);
3200         }
3201         //if(waitTimer){
3202         //    Roo.TaskMgr.stop(waitTimer);
3203         //    waitTimer = null;
3204         //}
3205     };
3206
3207     // private
3208     var updateButtons = function(b){
3209         var width = 0;
3210         if(!b){
3211             buttons["ok"].hide();
3212             buttons["cancel"].hide();
3213             buttons["yes"].hide();
3214             buttons["no"].hide();
3215             //dlg.footer.dom.style.display = 'none';
3216             return width;
3217         }
3218         dlg.footerEl.dom.style.display = '';
3219         for(var k in buttons){
3220             if(typeof buttons[k] != "function"){
3221                 if(b[k]){
3222                     buttons[k].show();
3223                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224                     width += buttons[k].el.getWidth()+15;
3225                 }else{
3226                     buttons[k].hide();
3227                 }
3228             }
3229         }
3230         return width;
3231     };
3232
3233     // private
3234     var handleEsc = function(d, k, e){
3235         if(opt && opt.closable !== false){
3236             dlg.hide();
3237         }
3238         if(e){
3239             e.stopEvent();
3240         }
3241     };
3242
3243     return {
3244         /**
3245          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246          * @return {Roo.BasicDialog} The BasicDialog element
3247          */
3248         getDialog : function(){
3249            if(!dlg){
3250                 dlg = new Roo.bootstrap.Modal( {
3251                     //draggable: true,
3252                     //resizable:false,
3253                     //constraintoviewport:false,
3254                     //fixedcenter:true,
3255                     //collapsible : false,
3256                     //shim:true,
3257                     //modal: true,
3258                 //    width: 'auto',
3259                   //  height:100,
3260                     //buttonAlign:"center",
3261                     closeClick : function(){
3262                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3263                             handleButton("no");
3264                         }else{
3265                             handleButton("cancel");
3266                         }
3267                     }
3268                 });
3269                 dlg.render();
3270                 dlg.on("hide", handleHide);
3271                 mask = dlg.mask;
3272                 //dlg.addKeyListener(27, handleEsc);
3273                 buttons = {};
3274                 this.buttons = buttons;
3275                 var bt = this.buttonText;
3276                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3280                 //Roo.log(buttons);
3281                 bodyEl = dlg.bodyEl.createChild({
3282
3283                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284                         '<textarea class="roo-mb-textarea"></textarea>' +
3285                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3286                 });
3287                 msgEl = bodyEl.dom.firstChild;
3288                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289                 textboxEl.enableDisplayMode();
3290                 textboxEl.addKeyListener([10,13], function(){
3291                     if(dlg.isVisible() && opt && opt.buttons){
3292                         if(opt.buttons.ok){
3293                             handleButton("ok");
3294                         }else if(opt.buttons.yes){
3295                             handleButton("yes");
3296                         }
3297                     }
3298                 });
3299                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300                 textareaEl.enableDisplayMode();
3301                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302                 progressEl.enableDisplayMode();
3303                 
3304                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305                 var pf = progressEl.dom.firstChild;
3306                 if (pf) {
3307                     pp = Roo.get(pf.firstChild);
3308                     pp.setHeight(pf.offsetHeight);
3309                 }
3310                 
3311             }
3312             return dlg;
3313         },
3314
3315         /**
3316          * Updates the message box body text
3317          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318          * the XHTML-compliant non-breaking space character '&amp;#160;')
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         updateText : function(text)
3322         {
3323             if(!dlg.isVisible() && !opt.width){
3324                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3326             }
3327             msgEl.innerHTML = text || '&#160;';
3328       
3329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3331             var w = Math.max(
3332                     Math.min(opt.width || cw , this.maxWidth), 
3333                     Math.max(opt.minWidth || this.minWidth, bwidth)
3334             );
3335             if(opt.prompt){
3336                 activeTextEl.setWidth(w);
3337             }
3338             if(dlg.isVisible()){
3339                 dlg.fixedcenter = false;
3340             }
3341             // to big, make it scroll. = But as usual stupid IE does not support
3342             // !important..
3343             
3344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3347             } else {
3348                 bodyEl.dom.style.height = '';
3349                 bodyEl.dom.style.overflowY = '';
3350             }
3351             if (cw > w) {
3352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3353             } else {
3354                 bodyEl.dom.style.overflowX = '';
3355             }
3356             
3357             dlg.setContentSize(w, bodyEl.getHeight());
3358             if(dlg.isVisible()){
3359                 dlg.fixedcenter = true;
3360             }
3361             return this;
3362         },
3363
3364         /**
3365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         updateProgress : function(value, text){
3372             if(text){
3373                 this.updateText(text);
3374             }
3375             
3376             if (pp) { // weird bug on my firefox - for some reason this is not defined
3377                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3379             }
3380             return this;
3381         },        
3382
3383         /**
3384          * Returns true if the message box is currently displayed
3385          * @return {Boolean} True if the message box is visible, else false
3386          */
3387         isVisible : function(){
3388             return dlg && dlg.isVisible();  
3389         },
3390
3391         /**
3392          * Hides the message box if it is displayed
3393          */
3394         hide : function(){
3395             if(this.isVisible()){
3396                 dlg.hide();
3397             }  
3398         },
3399
3400         /**
3401          * Displays a new message box, or reinitializes an existing message box, based on the config options
3402          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403          * The following config object properties are supported:
3404          * <pre>
3405 Property    Type             Description
3406 ----------  ---------------  ------------------------------------------------------------------------------------
3407 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3408                                    closes (defaults to undefined)
3409 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3412                                    progress and wait dialogs will ignore this property and always hide the
3413                                    close button as they can only be closed programmatically.
3414 cls               String           A custom CSS class to apply to the message box element
3415 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3416                                    displayed (defaults to 75)
3417 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3418                                    function will be btn (the name of the button that was clicked, if applicable,
3419                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3420                                    Progress and wait dialogs will ignore this option since they do not respond to
3421                                    user actions and can only be closed programmatically, so any required function
3422                                    should be called by the same code after it closes the dialog.
3423 icon              String           A CSS class that provides a background image to be used as an icon for
3424                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3426 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3427 modal             Boolean          False to allow user interaction with the page while the message box is
3428                                    displayed (defaults to true)
3429 msg               String           A string that will replace the existing message box body text (defaults
3430                                    to the XHTML-compliant non-breaking space character '&#160;')
3431 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3432 progress          Boolean          True to display a progress bar (defaults to false)
3433 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3436 title             String           The title text
3437 value             String           The string value to set into the active textbox element if displayed
3438 wait              Boolean          True to display a progress bar (defaults to false)
3439 width             Number           The width of the dialog in pixels
3440 </pre>
3441          *
3442          * Example usage:
3443          * <pre><code>
3444 Roo.Msg.show({
3445    title: 'Address',
3446    msg: 'Please enter your address:',
3447    width: 300,
3448    buttons: Roo.MessageBox.OKCANCEL,
3449    multiline: true,
3450    fn: saveAddress,
3451    animEl: 'addAddressBtn'
3452 });
3453 </code></pre>
3454          * @param {Object} config Configuration options
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         show : function(options)
3458         {
3459             
3460             // this causes nightmares if you show one dialog after another
3461             // especially on callbacks..
3462              
3463             if(this.isVisible()){
3464                 
3465                 this.hide();
3466                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3468                 Roo.log("New Dialog Message:" +  options.msg )
3469                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3471                 
3472             }
3473             var d = this.getDialog();
3474             opt = options;
3475             d.setTitle(opt.title || "&#160;");
3476             d.closeEl.setDisplayed(opt.closable !== false);
3477             activeTextEl = textboxEl;
3478             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3479             if(opt.prompt){
3480                 if(opt.multiline){
3481                     textboxEl.hide();
3482                     textareaEl.show();
3483                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3484                         opt.multiline : this.defaultTextHeight);
3485                     activeTextEl = textareaEl;
3486                 }else{
3487                     textboxEl.show();
3488                     textareaEl.hide();
3489                 }
3490             }else{
3491                 textboxEl.hide();
3492                 textareaEl.hide();
3493             }
3494             progressEl.setDisplayed(opt.progress === true);
3495             this.updateProgress(0);
3496             activeTextEl.dom.value = opt.value || "";
3497             if(opt.prompt){
3498                 dlg.setDefaultButton(activeTextEl);
3499             }else{
3500                 var bs = opt.buttons;
3501                 var db = null;
3502                 if(bs && bs.ok){
3503                     db = buttons["ok"];
3504                 }else if(bs && bs.yes){
3505                     db = buttons["yes"];
3506                 }
3507                 dlg.setDefaultButton(db);
3508             }
3509             bwidth = updateButtons(opt.buttons);
3510             this.updateText(opt.msg);
3511             if(opt.cls){
3512                 d.el.addClass(opt.cls);
3513             }
3514             d.proxyDrag = opt.proxyDrag === true;
3515             d.modal = opt.modal !== false;
3516             d.mask = opt.modal !== false ? mask : false;
3517             if(!d.isVisible()){
3518                 // force it to the end of the z-index stack so it gets a cursor in FF
3519                 document.body.appendChild(dlg.el.dom);
3520                 d.animateTarget = null;
3521                 d.show(options.animEl);
3522             }
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3528          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529          * and closing the message box when the process is complete.
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         progress : function(title, msg){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: false,
3539                 progress:true,
3540                 closable:false,
3541                 minWidth: this.minProgressWidth,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549          * If a callback function is passed it will be called after the user clicks the button, and the
3550          * id of the button that was clicked will be passed as the only parameter to the callback
3551          * (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         alert : function(title, msg, fn, scope)
3559         {
3560             this.show({
3561                 title : title,
3562                 msg : msg,
3563                 buttons: this.OK,
3564                 fn: fn,
3565                 closable : false,
3566                 scope : scope,
3567                 modal : true
3568             });
3569             return this;
3570         },
3571
3572         /**
3573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575          * You are responsible for closing the message box when the process is complete.
3576          * @param {String} msg The message box body text
3577          * @param {String} title (optional) The title bar text
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         wait : function(msg, title){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: false,
3585                 closable:false,
3586                 progress:true,
3587                 modal:true,
3588                 width:300,
3589                 wait:true
3590             });
3591             waitTimer = Roo.TaskMgr.start({
3592                 run: function(i){
3593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594                 },
3595                 interval: 1000
3596             });
3597             return this;
3598         },
3599
3600         /**
3601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604          * @param {String} title The title bar text
3605          * @param {String} msg The message box body text
3606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607          * @param {Object} scope (optional) The scope of the callback function
3608          * @return {Roo.MessageBox} This message box
3609          */
3610         confirm : function(title, msg, fn, scope){
3611             this.show({
3612                 title : title,
3613                 msg : msg,
3614                 buttons: this.YESNO,
3615                 fn: fn,
3616                 scope : scope,
3617                 modal : true
3618             });
3619             return this;
3620         },
3621
3622         /**
3623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626          * (could also be the top-right close button) and the text that was entered will be passed as the two
3627          * parameters to the callback.
3628          * @param {String} title The title bar text
3629          * @param {String} msg The message box body text
3630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631          * @param {Object} scope (optional) The scope of the callback function
3632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634          * @return {Roo.MessageBox} This message box
3635          */
3636         prompt : function(title, msg, fn, scope, multiline){
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OKCANCEL,
3641                 fn: fn,
3642                 minWidth:250,
3643                 scope : scope,
3644                 prompt:true,
3645                 multiline: multiline,
3646                 modal : true
3647             });
3648             return this;
3649         },
3650
3651         /**
3652          * Button config that displays a single OK button
3653          * @type Object
3654          */
3655         OK : {ok:true},
3656         /**
3657          * Button config that displays Yes and No buttons
3658          * @type Object
3659          */
3660         YESNO : {yes:true, no:true},
3661         /**
3662          * Button config that displays OK and Cancel buttons
3663          * @type Object
3664          */
3665         OKCANCEL : {ok:true, cancel:true},
3666         /**
3667          * Button config that displays Yes, No and Cancel buttons
3668          * @type Object
3669          */
3670         YESNOCANCEL : {yes:true, no:true, cancel:true},
3671
3672         /**
3673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3674          * @type Number
3675          */
3676         defaultTextHeight : 75,
3677         /**
3678          * The maximum width in pixels of the message box (defaults to 600)
3679          * @type Number
3680          */
3681         maxWidth : 600,
3682         /**
3683          * The minimum width in pixels of the message box (defaults to 100)
3684          * @type Number
3685          */
3686         minWidth : 100,
3687         /**
3688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3690          * @type Number
3691          */
3692         minProgressWidth : 250,
3693         /**
3694          * An object containing the default button text strings that can be overriden for localized language support.
3695          * Supported properties are: ok, cancel, yes and no.
3696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3697          * @type Object
3698          */
3699         buttonText : {
3700             ok : "OK",
3701             cancel : "Cancel",
3702             yes : "Yes",
3703             no : "No"
3704         }
3705     };
3706 }();
3707
3708 /**
3709  * Shorthand for {@link Roo.MessageBox}
3710  */
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3713 /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.Navbar
3722  * @extends Roo.bootstrap.Component
3723  * Bootstrap Navbar class
3724
3725  * @constructor
3726  * Create a new Navbar
3727  * @param {Object} config The config object
3728  */
3729
3730
3731 Roo.bootstrap.Navbar = function(config){
3732     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3733     this.addEvents({
3734         // raw events
3735         /**
3736          * @event beforetoggle
3737          * Fire before toggle the menu
3738          * @param {Roo.EventObject} e
3739          */
3740         "beforetoggle" : true
3741     });
3742 };
3743
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3745     
3746     
3747    
3748     // private
3749     navItems : false,
3750     loadMask : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3757         
3758     },
3759     
3760     initEvents :function ()
3761     {
3762         //Roo.log(this.el.select('.navbar-toggle',true));
3763         this.el.select('.navbar-toggle',true).on('click', function() {
3764             if(this.fireEvent('beforetoggle', this) !== false){
3765                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3766             }
3767             
3768         }, this);
3769         
3770         var mark = {
3771             tag: "div",
3772             cls:"x-dlg-mask"
3773         };
3774         
3775         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3776         
3777         var size = this.el.getSize();
3778         this.maskEl.setSize(size.width, size.height);
3779         this.maskEl.enableDisplayMode("block");
3780         this.maskEl.hide();
3781         
3782         if(this.loadMask){
3783             this.maskEl.show();
3784         }
3785     },
3786     
3787     
3788     getChildContainer : function()
3789     {
3790         if (this.el.select('.collapse').getCount()) {
3791             return this.el.select('.collapse',true).first();
3792         }
3793         
3794         return this.el;
3795     },
3796     
3797     mask : function()
3798     {
3799         this.maskEl.show();
3800     },
3801     
3802     unmask : function()
3803     {
3804         this.maskEl.hide();
3805     } 
3806     
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  /*
3817  * - LGPL
3818  *
3819  * navbar
3820  * 
3821  */
3822
3823 /**
3824  * @class Roo.bootstrap.NavSimplebar
3825  * @extends Roo.bootstrap.Navbar
3826  * Bootstrap Sidebar class
3827  *
3828  * @cfg {Boolean} inverse is inverted color
3829  * 
3830  * @cfg {String} type (nav | pills | tabs)
3831  * @cfg {Boolean} arrangement stacked | justified
3832  * @cfg {String} align (left | right) alignment
3833  * 
3834  * @cfg {Boolean} main (true|false) main nav bar? default false
3835  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3836  * 
3837  * @cfg {String} tag (header|footer|nav|div) default is nav 
3838
3839  * 
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new Sidebar
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.NavSimplebar = function(config){
3849     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3853     
3854     inverse: false,
3855     
3856     type: false,
3857     arrangement: '',
3858     align : false,
3859     
3860     
3861     
3862     main : false,
3863     
3864     
3865     tag : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         var cfg = {
3872             tag : this.tag || 'div',
3873             cls : 'navbar'
3874         };
3875           
3876         
3877         cfg.cn = [
3878             {
3879                 cls: 'nav',
3880                 tag : 'ul'
3881             }
3882         ];
3883         
3884          
3885         this.type = this.type || 'nav';
3886         if (['tabs','pills'].indexOf(this.type)!==-1) {
3887             cfg.cn[0].cls += ' nav-' + this.type
3888         
3889         
3890         } else {
3891             if (this.type!=='nav') {
3892                 Roo.log('nav type must be nav/tabs/pills')
3893             }
3894             cfg.cn[0].cls += ' navbar-nav'
3895         }
3896         
3897         
3898         
3899         
3900         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.arrangement;
3902         }
3903         
3904         
3905         if (this.align === 'right') {
3906             cfg.cn[0].cls += ' navbar-right';
3907         }
3908         
3909         if (this.inverse) {
3910             cfg.cls += ' navbar-inverse';
3911             
3912         }
3913         
3914         
3915         return cfg;
3916     
3917         
3918     }
3919     
3920     
3921     
3922 });
3923
3924
3925
3926  
3927
3928  
3929        /*
3930  * - LGPL
3931  *
3932  * navbar
3933  * 
3934  */
3935
3936 /**
3937  * @class Roo.bootstrap.NavHeaderbar
3938  * @extends Roo.bootstrap.NavSimplebar
3939  * Bootstrap Sidebar class
3940  *
3941  * @cfg {String} brand what is brand
3942  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943  * @cfg {String} brand_href href of the brand
3944  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3945  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3948  * 
3949  * @constructor
3950  * Create a new Sidebar
3951  * @param {Object} config The config object
3952  */
3953
3954
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3957       
3958 };
3959
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3961     
3962     position: '',
3963     brand: '',
3964     brand_href: false,
3965     srButton : true,
3966     autohide : false,
3967     desktopCenter : false,
3968    
3969     
3970     getAutoCreate : function(){
3971         
3972         var   cfg = {
3973             tag: this.nav || 'nav',
3974             cls: 'navbar',
3975             role: 'navigation',
3976             cn: []
3977         };
3978         
3979         var cn = cfg.cn;
3980         if (this.desktopCenter) {
3981             cn.push({cls : 'container', cn : []});
3982             cn = cn[0].cn;
3983         }
3984         
3985         if(this.srButton){
3986             cn.push({
3987                 tag: 'div',
3988                 cls: 'navbar-header',
3989                 cn: [
3990                     {
3991                         tag: 'button',
3992                         type: 'button',
3993                         cls: 'navbar-toggle',
3994                         'data-toggle': 'collapse',
3995                         cn: [
3996                             {
3997                                 tag: 'span',
3998                                 cls: 'sr-only',
3999                                 html: 'Toggle navigation'
4000                             },
4001                             {
4002                                 tag: 'span',
4003                                 cls: 'icon-bar'
4004                             },
4005                             {
4006                                 tag: 'span',
4007                                 cls: 'icon-bar'
4008                             },
4009                             {
4010                                 tag: 'span',
4011                                 cls: 'icon-bar'
4012                             }
4013                         ]
4014                     }
4015                 ]
4016             });
4017         }
4018         
4019         cn.push({
4020             tag: 'div',
4021             cls: 'collapse navbar-collapse',
4022             cn : []
4023         });
4024         
4025         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4026         
4027         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028             cfg.cls += ' navbar-' + this.position;
4029             
4030             // tag can override this..
4031             
4032             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4033         }
4034         
4035         if (this.brand !== '') {
4036             cn[0].cn.push({
4037                 tag: 'a',
4038                 href: this.brand_href ? this.brand_href : '#',
4039                 cls: 'navbar-brand',
4040                 cn: [
4041                 this.brand
4042                 ]
4043             });
4044         }
4045         
4046         if(this.main){
4047             cfg.cls += ' main-nav';
4048         }
4049         
4050         
4051         return cfg;
4052
4053         
4054     },
4055     getHeaderChildContainer : function()
4056     {
4057         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058             return this.el.select('.navbar-header',true).first();
4059         }
4060         
4061         return this.getChildContainer();
4062     },
4063     
4064     
4065     initEvents : function()
4066     {
4067         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4068         
4069         if (this.autohide) {
4070             
4071             var prevScroll = 0;
4072             var ft = this.el;
4073             
4074             Roo.get(document).on('scroll',function(e) {
4075                 var ns = Roo.get(document).getScroll().top;
4076                 var os = prevScroll;
4077                 prevScroll = ns;
4078                 
4079                 if(ns > os){
4080                     ft.removeClass('slideDown');
4081                     ft.addClass('slideUp');
4082                     return;
4083                 }
4084                 ft.removeClass('slideUp');
4085                 ft.addClass('slideDown');
4086                  
4087               
4088           },this);
4089         }
4090     }    
4091     
4092 });
4093
4094
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * navbar
4102  * 
4103  */
4104
4105 /**
4106  * @class Roo.bootstrap.NavSidebar
4107  * @extends Roo.bootstrap.Navbar
4108  * Bootstrap Sidebar class
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavSidebar = function(config){
4117     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4118 };
4119
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4121     
4122     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4123     
4124     getAutoCreate : function(){
4125         
4126         
4127         return  {
4128             tag: 'div',
4129             cls: 'sidebar sidebar-nav'
4130         };
4131     
4132         
4133     }
4134     
4135     
4136     
4137 });
4138
4139
4140
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * nav group
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.NavGroup
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap NavGroup class
4154  * @cfg {String} align (left|right)
4155  * @cfg {Boolean} inverse
4156  * @cfg {String} type (nav|pills|tab) default nav
4157  * @cfg {String} navId - reference Id for navbar.
4158
4159  * 
4160  * @constructor
4161  * Create a new nav group
4162  * @param {Object} config The config object
4163  */
4164
4165 Roo.bootstrap.NavGroup = function(config){
4166     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4167     this.navItems = [];
4168    
4169     Roo.bootstrap.NavGroup.register(this);
4170      this.addEvents({
4171         /**
4172              * @event changed
4173              * Fires when the active item changes
4174              * @param {Roo.bootstrap.NavGroup} this
4175              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4177          */
4178         'changed': true
4179      });
4180     
4181 };
4182
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4184     
4185     align: '',
4186     inverse: false,
4187     form: false,
4188     type: 'nav',
4189     navId : '',
4190     // private
4191     
4192     navItems : false, 
4193     
4194     getAutoCreate : function()
4195     {
4196         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4197         
4198         cfg = {
4199             tag : 'ul',
4200             cls: 'nav' 
4201         };
4202         
4203         if (['tabs','pills'].indexOf(this.type)!==-1) {
4204             cfg.cls += ' nav-' + this.type
4205         } else {
4206             if (this.type!=='nav') {
4207                 Roo.log('nav type must be nav/tabs/pills')
4208             }
4209             cfg.cls += ' navbar-nav'
4210         }
4211         
4212         if (this.parent() && this.parent().sidebar) {
4213             cfg = {
4214                 tag: 'ul',
4215                 cls: 'dashboard-menu sidebar-menu'
4216             };
4217             
4218             return cfg;
4219         }
4220         
4221         if (this.form === true) {
4222             cfg = {
4223                 tag: 'form',
4224                 cls: 'navbar-form'
4225             };
4226             
4227             if (this.align === 'right') {
4228                 cfg.cls += ' navbar-right';
4229             } else {
4230                 cfg.cls += ' navbar-left';
4231             }
4232         }
4233         
4234         if (this.align === 'right') {
4235             cfg.cls += ' navbar-right';
4236         }
4237         
4238         if (this.inverse) {
4239             cfg.cls += ' navbar-inverse';
4240             
4241         }
4242         
4243         
4244         return cfg;
4245     },
4246     /**
4247     * sets the active Navigation item
4248     * @param {Roo.bootstrap.NavItem} the new current navitem
4249     */
4250     setActiveItem : function(item)
4251     {
4252         var prev = false;
4253         Roo.each(this.navItems, function(v){
4254             if (v == item) {
4255                 return ;
4256             }
4257             if (v.isActive()) {
4258                 v.setActive(false, true);
4259                 prev = v;
4260                 
4261             }
4262             
4263         });
4264
4265         item.setActive(true, true);
4266         this.fireEvent('changed', this, item, prev);
4267         
4268         
4269     },
4270     /**
4271     * gets the active Navigation item
4272     * @return {Roo.bootstrap.NavItem} the current navitem
4273     */
4274     getActive : function()
4275     {
4276         
4277         var prev = false;
4278         Roo.each(this.navItems, function(v){
4279             
4280             if (v.isActive()) {
4281                 prev = v;
4282                 
4283             }
4284             
4285         });
4286         return prev;
4287     },
4288     
4289     indexOfNav : function()
4290     {
4291         
4292         var prev = false;
4293         Roo.each(this.navItems, function(v,i){
4294             
4295             if (v.isActive()) {
4296                 prev = i;
4297                 
4298             }
4299             
4300         });
4301         return prev;
4302     },
4303     /**
4304     * adds a Navigation item
4305     * @param {Roo.bootstrap.NavItem} the navitem to add
4306     */
4307     addItem : function(cfg)
4308     {
4309         var cn = new Roo.bootstrap.NavItem(cfg);
4310         this.register(cn);
4311         cn.parentId = this.id;
4312         cn.onRender(this.el, null);
4313         return cn;
4314     },
4315     /**
4316     * register a Navigation item
4317     * @param {Roo.bootstrap.NavItem} the navitem to add
4318     */
4319     register : function(item)
4320     {
4321         this.navItems.push( item);
4322         item.navId = this.navId;
4323     
4324     },
4325     
4326     /**
4327     * clear all the Navigation item
4328     */
4329    
4330     clearAll : function()
4331     {
4332         this.navItems = [];
4333         this.el.dom.innerHTML = '';
4334     },
4335     
4336     getNavItem: function(tabId)
4337     {
4338         var ret = false;
4339         Roo.each(this.navItems, function(e) {
4340             if (e.tabId == tabId) {
4341                ret =  e;
4342                return false;
4343             }
4344             return true;
4345             
4346         });
4347         return ret;
4348     },
4349     
4350     setActiveNext : function()
4351     {
4352         var i = this.indexOfNav(this.getActive());
4353         if (i > this.navItems.length) {
4354             return;
4355         }
4356         this.setActiveItem(this.navItems[i+1]);
4357     },
4358     setActivePrev : function()
4359     {
4360         var i = this.indexOfNav(this.getActive());
4361         if (i  < 1) {
4362             return;
4363         }
4364         this.setActiveItem(this.navItems[i-1]);
4365     },
4366     clearWasActive : function(except) {
4367         Roo.each(this.navItems, function(e) {
4368             if (e.tabId != except.tabId && e.was_active) {
4369                e.was_active = false;
4370                return false;
4371             }
4372             return true;
4373             
4374         });
4375     },
4376     getWasActive : function ()
4377     {
4378         var r = false;
4379         Roo.each(this.navItems, function(e) {
4380             if (e.was_active) {
4381                r = e;
4382                return false;
4383             }
4384             return true;
4385             
4386         });
4387         return r;
4388     }
4389     
4390     
4391 });
4392
4393  
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4395     
4396     groups: {},
4397      /**
4398     * register a Navigation Group
4399     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4400     */
4401     register : function(navgrp)
4402     {
4403         this.groups[navgrp.navId] = navgrp;
4404         
4405     },
4406     /**
4407     * fetch a Navigation Group based on the navigation ID
4408     * @param {string} the navgroup to add
4409     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4410     */
4411     get: function(navId) {
4412         if (typeof(this.groups[navId]) == 'undefined') {
4413             return false;
4414             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4415         }
4416         return this.groups[navId] ;
4417     }
4418     
4419     
4420     
4421 });
4422
4423  /*
4424  * - LGPL
4425  *
4426  * row
4427  * 
4428  */
4429
4430 /**
4431  * @class Roo.bootstrap.NavItem
4432  * @extends Roo.bootstrap.Component
4433  * Bootstrap Navbar.NavItem class
4434  * @cfg {String} href  link to
4435  * @cfg {String} html content of button
4436  * @cfg {String} badge text inside badge
4437  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438  * @cfg {String} glyphicon name of glyphicon
4439  * @cfg {String} icon name of font awesome icon
4440  * @cfg {Boolean} active Is item active
4441  * @cfg {Boolean} disabled Is item disabled
4442  
4443  * @cfg {Boolean} preventDefault (true | false) default false
4444  * @cfg {String} tabId the tab that this item activates.
4445  * @cfg {String} tagtype (a|span) render as a href or span?
4446  * @cfg {Boolean} animateRef (true|false) link to element default false  
4447   
4448  * @constructor
4449  * Create a new Navbar Item
4450  * @param {Object} config The config object
4451  */
4452 Roo.bootstrap.NavItem = function(config){
4453     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4454     this.addEvents({
4455         // raw events
4456         /**
4457          * @event click
4458          * The raw click event for the entire grid.
4459          * @param {Roo.EventObject} e
4460          */
4461         "click" : true,
4462          /**
4463             * @event changed
4464             * Fires when the active item active state changes
4465             * @param {Roo.bootstrap.NavItem} this
4466             * @param {boolean} state the new state
4467              
4468          */
4469         'changed': true,
4470         /**
4471             * @event scrollto
4472             * Fires when scroll to element
4473             * @param {Roo.bootstrap.NavItem} this
4474             * @param {Object} options
4475             * @param {Roo.EventObject} e
4476              
4477          */
4478         'scrollto': true
4479     });
4480    
4481 };
4482
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4484     
4485     href: false,
4486     html: '',
4487     badge: '',
4488     icon: false,
4489     glyphicon: false,
4490     active: false,
4491     preventDefault : false,
4492     tabId : false,
4493     tagtype : 'a',
4494     disabled : false,
4495     animateRef : false,
4496     was_active : false,
4497     
4498     getAutoCreate : function(){
4499          
4500         var cfg = {
4501             tag: 'li',
4502             cls: 'nav-item'
4503             
4504         };
4505         
4506         if (this.active) {
4507             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4508         }
4509         if (this.disabled) {
4510             cfg.cls += ' disabled';
4511         }
4512         
4513         if (this.href || this.html || this.glyphicon || this.icon) {
4514             cfg.cn = [
4515                 {
4516                     tag: this.tagtype,
4517                     href : this.href || "#",
4518                     html: this.html || ''
4519                 }
4520             ];
4521             
4522             if (this.icon) {
4523                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4524             }
4525
4526             if(this.glyphicon) {
4527                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4528             }
4529             
4530             if (this.menu) {
4531                 
4532                 cfg.cn[0].html += " <span class='caret'></span>";
4533              
4534             }
4535             
4536             if (this.badge !== '') {
4537                  
4538                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4539             }
4540         }
4541         
4542         
4543         
4544         return cfg;
4545     },
4546     initEvents: function() 
4547     {
4548         if (typeof (this.menu) != 'undefined') {
4549             this.menu.parentType = this.xtype;
4550             this.menu.triggerEl = this.el;
4551             this.menu = this.addxtype(Roo.apply({}, this.menu));
4552         }
4553         
4554         this.el.select('a',true).on('click', this.onClick, this);
4555         
4556         if(this.tagtype == 'span'){
4557             this.el.select('span',true).on('click', this.onClick, this);
4558         }
4559        
4560         // at this point parent should be available..
4561         this.parent().register(this);
4562     },
4563     
4564     onClick : function(e)
4565     {
4566         if (e.getTarget('.dropdown-menu-item')) {
4567             // did you click on a menu itemm.... - then don't trigger onclick..
4568             return;
4569         }
4570         
4571         if(
4572                 this.preventDefault || 
4573                 this.href == '#' 
4574         ){
4575             Roo.log("NavItem - prevent Default?");
4576             e.preventDefault();
4577         }
4578         
4579         if (this.disabled) {
4580             return;
4581         }
4582         
4583         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584         if (tg && tg.transition) {
4585             Roo.log("waiting for the transitionend");
4586             return;
4587         }
4588         
4589         
4590         
4591         //Roo.log("fire event clicked");
4592         if(this.fireEvent('click', this, e) === false){
4593             return;
4594         };
4595         
4596         if(this.tagtype == 'span'){
4597             return;
4598         }
4599         
4600         //Roo.log(this.href);
4601         var ael = this.el.select('a',true).first();
4602         //Roo.log(ael);
4603         
4604         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607                 return; // ignore... - it's a 'hash' to another page.
4608             }
4609             Roo.log("NavItem - prevent Default?");
4610             e.preventDefault();
4611             this.scrollToElement(e);
4612         }
4613         
4614         
4615         var p =  this.parent();
4616    
4617         if (['tabs','pills'].indexOf(p.type)!==-1) {
4618             if (typeof(p.setActiveItem) !== 'undefined') {
4619                 p.setActiveItem(this);
4620             }
4621         }
4622         
4623         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625             // remove the collapsed menu expand...
4626             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4627         }
4628     },
4629     
4630     isActive: function () {
4631         return this.active
4632     },
4633     setActive : function(state, fire, is_was_active)
4634     {
4635         if (this.active && !state && this.navId) {
4636             this.was_active = true;
4637             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4638             if (nv) {
4639                 nv.clearWasActive(this);
4640             }
4641             
4642         }
4643         this.active = state;
4644         
4645         if (!state ) {
4646             this.el.removeClass('active');
4647         } else if (!this.el.hasClass('active')) {
4648             this.el.addClass('active');
4649         }
4650         if (fire) {
4651             this.fireEvent('changed', this, state);
4652         }
4653         
4654         // show a panel if it's registered and related..
4655         
4656         if (!this.navId || !this.tabId || !state || is_was_active) {
4657             return;
4658         }
4659         
4660         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4661         if (!tg) {
4662             return;
4663         }
4664         var pan = tg.getPanelByName(this.tabId);
4665         if (!pan) {
4666             return;
4667         }
4668         // if we can not flip to new panel - go back to old nav highlight..
4669         if (false == tg.showPanel(pan)) {
4670             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4671             if (nv) {
4672                 var onav = nv.getWasActive();
4673                 if (onav) {
4674                     onav.setActive(true, false, true);
4675                 }
4676             }
4677             
4678         }
4679         
4680         
4681         
4682     },
4683      // this should not be here...
4684     setDisabled : function(state)
4685     {
4686         this.disabled = state;
4687         if (!state ) {
4688             this.el.removeClass('disabled');
4689         } else if (!this.el.hasClass('disabled')) {
4690             this.el.addClass('disabled');
4691         }
4692         
4693     },
4694     
4695     /**
4696      * Fetch the element to display the tooltip on.
4697      * @return {Roo.Element} defaults to this.el
4698      */
4699     tooltipEl : function()
4700     {
4701         return this.el.select('' + this.tagtype + '', true).first();
4702     },
4703     
4704     scrollToElement : function(e)
4705     {
4706         var c = document.body;
4707         
4708         /*
4709          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4710          */
4711         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712             c = document.documentElement;
4713         }
4714         
4715         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4716         
4717         if(!target){
4718             return;
4719         }
4720
4721         var o = target.calcOffsetsTo(c);
4722         
4723         var options = {
4724             target : target,
4725             value : o[1]
4726         };
4727         
4728         this.fireEvent('scrollto', this, options, e);
4729         
4730         Roo.get(c).scrollTo('top', options.value, true);
4731         
4732         return;
4733     }
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * sidebar item
4741  *
4742  *  li
4743  *    <span> icon </span>
4744  *    <span> text </span>
4745  *    <span>badge </span>
4746  */
4747
4748 /**
4749  * @class Roo.bootstrap.NavSidebarItem
4750  * @extends Roo.bootstrap.NavItem
4751  * Bootstrap Navbar.NavSidebarItem class
4752  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753  * {Boolean} open is the menu open
4754  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756  * {String} buttonSize (sm|md|lg)the extra classes for the button
4757  * {Boolean} showArrow show arrow next to the text (default true)
4758  * @constructor
4759  * Create a new Navbar Button
4760  * @param {Object} config The config object
4761  */
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4764     this.addEvents({
4765         // raw events
4766         /**
4767          * @event click
4768          * The raw click event for the entire grid.
4769          * @param {Roo.EventObject} e
4770          */
4771         "click" : true,
4772          /**
4773             * @event changed
4774             * Fires when the active item active state changes
4775             * @param {Roo.bootstrap.NavSidebarItem} this
4776             * @param {boolean} state the new state
4777              
4778          */
4779         'changed': true
4780     });
4781    
4782 };
4783
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4785     
4786     badgeWeight : 'default',
4787     
4788     open: false,
4789     
4790     buttonView : false,
4791     
4792     buttonWeight : 'default',
4793     
4794     buttonSize : 'md',
4795     
4796     showArrow : true,
4797     
4798     getAutoCreate : function(){
4799         
4800         
4801         var a = {
4802                 tag: 'a',
4803                 href : this.href || '#',
4804                 cls: '',
4805                 html : '',
4806                 cn : []
4807         };
4808         
4809         if(this.buttonView){
4810             a = {
4811                 tag: 'button',
4812                 href : this.href || '#',
4813                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4814                 html : this.html,
4815                 cn : []
4816             };
4817         }
4818         
4819         var cfg = {
4820             tag: 'li',
4821             cls: '',
4822             cn: [ a ]
4823         };
4824         
4825         if (this.active) {
4826             cfg.cls += ' active';
4827         }
4828         
4829         if (this.disabled) {
4830             cfg.cls += ' disabled';
4831         }
4832         if (this.open) {
4833             cfg.cls += ' open x-open';
4834         }
4835         // left icon..
4836         if (this.glyphicon || this.icon) {
4837             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4838             a.cn.push({ tag : 'i', cls : c }) ;
4839         }
4840         
4841         if(!this.buttonView){
4842             var span = {
4843                 tag: 'span',
4844                 html : this.html || ''
4845             };
4846
4847             a.cn.push(span);
4848             
4849         }
4850         
4851         if (this.badge !== '') {
4852             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4853         }
4854         
4855         if (this.menu) {
4856             
4857             if(this.showArrow){
4858                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4859             }
4860             
4861             a.cls += ' dropdown-toggle treeview' ;
4862         }
4863         
4864         return cfg;
4865     },
4866     
4867     initEvents : function()
4868     { 
4869         if (typeof (this.menu) != 'undefined') {
4870             this.menu.parentType = this.xtype;
4871             this.menu.triggerEl = this.el;
4872             this.menu = this.addxtype(Roo.apply({}, this.menu));
4873         }
4874         
4875         this.el.on('click', this.onClick, this);
4876         
4877         if(this.badge !== ''){
4878             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4879         }
4880         
4881     },
4882     
4883     onClick : function(e)
4884     {
4885         if(this.disabled){
4886             e.preventDefault();
4887             return;
4888         }
4889         
4890         if(this.preventDefault){
4891             e.preventDefault();
4892         }
4893         
4894         this.fireEvent('click', this);
4895     },
4896     
4897     disable : function()
4898     {
4899         this.setDisabled(true);
4900     },
4901     
4902     enable : function()
4903     {
4904         this.setDisabled(false);
4905     },
4906     
4907     setDisabled : function(state)
4908     {
4909         if(this.disabled == state){
4910             return;
4911         }
4912         
4913         this.disabled = state;
4914         
4915         if (state) {
4916             this.el.addClass('disabled');
4917             return;
4918         }
4919         
4920         this.el.removeClass('disabled');
4921         
4922         return;
4923     },
4924     
4925     setActive : function(state)
4926     {
4927         if(this.active == state){
4928             return;
4929         }
4930         
4931         this.active = state;
4932         
4933         if (state) {
4934             this.el.addClass('active');
4935             return;
4936         }
4937         
4938         this.el.removeClass('active');
4939         
4940         return;
4941     },
4942     
4943     isActive: function () 
4944     {
4945         return this.active;
4946     },
4947     
4948     setBadge : function(str)
4949     {
4950         if(!this.badgeEl){
4951             return;
4952         }
4953         
4954         this.badgeEl.dom.innerHTML = str;
4955     }
4956     
4957    
4958      
4959  
4960 });
4961  
4962
4963  /*
4964  * - LGPL
4965  *
4966  * row
4967  * 
4968  */
4969
4970 /**
4971  * @class Roo.bootstrap.Row
4972  * @extends Roo.bootstrap.Component
4973  * Bootstrap Row class (contains columns...)
4974  * 
4975  * @constructor
4976  * Create a new Row
4977  * @param {Object} config The config object
4978  */
4979
4980 Roo.bootstrap.Row = function(config){
4981     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4982 };
4983
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4985     
4986     getAutoCreate : function(){
4987        return {
4988             cls: 'row clearfix'
4989        };
4990     }
4991     
4992     
4993 });
4994
4995  
4996
4997  /*
4998  * - LGPL
4999  *
5000  * element
5001  * 
5002  */
5003
5004 /**
5005  * @class Roo.bootstrap.Element
5006  * @extends Roo.bootstrap.Component
5007  * Bootstrap Element class
5008  * @cfg {String} html contents of the element
5009  * @cfg {String} tag tag of the element
5010  * @cfg {String} cls class of the element
5011  * @cfg {Boolean} preventDefault (true|false) default false
5012  * @cfg {Boolean} clickable (true|false) default false
5013  * 
5014  * @constructor
5015  * Create a new Element
5016  * @param {Object} config The config object
5017  */
5018
5019 Roo.bootstrap.Element = function(config){
5020     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5021     
5022     this.addEvents({
5023         // raw events
5024         /**
5025          * @event click
5026          * When a element is chick
5027          * @param {Roo.bootstrap.Element} this
5028          * @param {Roo.EventObject} e
5029          */
5030         "click" : true
5031     });
5032 };
5033
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5035     
5036     tag: 'div',
5037     cls: '',
5038     html: '',
5039     preventDefault: false, 
5040     clickable: false,
5041     
5042     getAutoCreate : function(){
5043         
5044         var cfg = {
5045             tag: this.tag,
5046             // cls: this.cls, double assign in parent class Component.js :: onRender
5047             html: this.html
5048         };
5049         
5050         return cfg;
5051     },
5052     
5053     initEvents: function() 
5054     {
5055         Roo.bootstrap.Element.superclass.initEvents.call(this);
5056         
5057         if(this.clickable){
5058             this.el.on('click', this.onClick, this);
5059         }
5060         
5061     },
5062     
5063     onClick : function(e)
5064     {
5065         if(this.preventDefault){
5066             e.preventDefault();
5067         }
5068         
5069         this.fireEvent('click', this, e);
5070     },
5071     
5072     getValue : function()
5073     {
5074         return this.el.dom.innerHTML;
5075     },
5076     
5077     setValue : function(value)
5078     {
5079         this.el.dom.innerHTML = value;
5080     }
5081    
5082 });
5083
5084  
5085
5086  /*
5087  * - LGPL
5088  *
5089  * pagination
5090  * 
5091  */
5092
5093 /**
5094  * @class Roo.bootstrap.Pagination
5095  * @extends Roo.bootstrap.Component
5096  * Bootstrap Pagination class
5097  * @cfg {String} size xs | sm | md | lg
5098  * @cfg {Boolean} inverse false | true
5099  * 
5100  * @constructor
5101  * Create a new Pagination
5102  * @param {Object} config The config object
5103  */
5104
5105 Roo.bootstrap.Pagination = function(config){
5106     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5107 };
5108
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5110     
5111     cls: false,
5112     size: false,
5113     inverse: false,
5114     
5115     getAutoCreate : function(){
5116         var cfg = {
5117             tag: 'ul',
5118                 cls: 'pagination'
5119         };
5120         if (this.inverse) {
5121             cfg.cls += ' inverse';
5122         }
5123         if (this.html) {
5124             cfg.html=this.html;
5125         }
5126         if (this.cls) {
5127             cfg.cls += " " + this.cls;
5128         }
5129         return cfg;
5130     }
5131    
5132 });
5133
5134  
5135
5136  /*
5137  * - LGPL
5138  *
5139  * Pagination item
5140  * 
5141  */
5142
5143
5144 /**
5145  * @class Roo.bootstrap.PaginationItem
5146  * @extends Roo.bootstrap.Component
5147  * Bootstrap PaginationItem class
5148  * @cfg {String} html text
5149  * @cfg {String} href the link
5150  * @cfg {Boolean} preventDefault (true | false) default true
5151  * @cfg {Boolean} active (true | false) default false
5152  * @cfg {Boolean} disabled default false
5153  * 
5154  * 
5155  * @constructor
5156  * Create a new PaginationItem
5157  * @param {Object} config The config object
5158  */
5159
5160
5161 Roo.bootstrap.PaginationItem = function(config){
5162     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5163     this.addEvents({
5164         // raw events
5165         /**
5166          * @event click
5167          * The raw click event for the entire grid.
5168          * @param {Roo.EventObject} e
5169          */
5170         "click" : true
5171     });
5172 };
5173
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5175     
5176     href : false,
5177     html : false,
5178     preventDefault: true,
5179     active : false,
5180     cls : false,
5181     disabled: false,
5182     
5183     getAutoCreate : function(){
5184         var cfg= {
5185             tag: 'li',
5186             cn: [
5187                 {
5188                     tag : 'a',
5189                     href : this.href ? this.href : '#',
5190                     html : this.html ? this.html : ''
5191                 }
5192             ]
5193         };
5194         
5195         if(this.cls){
5196             cfg.cls = this.cls;
5197         }
5198         
5199         if(this.disabled){
5200             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5201         }
5202         
5203         if(this.active){
5204             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5205         }
5206         
5207         return cfg;
5208     },
5209     
5210     initEvents: function() {
5211         
5212         this.el.on('click', this.onClick, this);
5213         
5214     },
5215     onClick : function(e)
5216     {
5217         Roo.log('PaginationItem on click ');
5218         if(this.preventDefault){
5219             e.preventDefault();
5220         }
5221         
5222         if(this.disabled){
5223             return;
5224         }
5225         
5226         this.fireEvent('click', this, e);
5227     }
5228    
5229 });
5230
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * slider
5237  * 
5238  */
5239
5240
5241 /**
5242  * @class Roo.bootstrap.Slider
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Slider class
5245  *    
5246  * @constructor
5247  * Create a new Slider
5248  * @param {Object} config The config object
5249  */
5250
5251 Roo.bootstrap.Slider = function(config){
5252     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5253 };
5254
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5256     
5257     getAutoCreate : function(){
5258         
5259         var cfg = {
5260             tag: 'div',
5261             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5262             cn: [
5263                 {
5264                     tag: 'a',
5265                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5266                 }
5267             ]
5268         };
5269         
5270         return cfg;
5271     }
5272    
5273 });
5274
5275  /*
5276  * Based on:
5277  * Ext JS Library 1.1.1
5278  * Copyright(c) 2006-2007, Ext JS, LLC.
5279  *
5280  * Originally Released Under LGPL - original licence link has changed is not relivant.
5281  *
5282  * Fork - LGPL
5283  * <script type="text/javascript">
5284  */
5285  
5286
5287 /**
5288  * @class Roo.grid.ColumnModel
5289  * @extends Roo.util.Observable
5290  * This is the default implementation of a ColumnModel used by the Grid. It defines
5291  * the columns in the grid.
5292  * <br>Usage:<br>
5293  <pre><code>
5294  var colModel = new Roo.grid.ColumnModel([
5295         {header: "Ticker", width: 60, sortable: true, locked: true},
5296         {header: "Company Name", width: 150, sortable: true},
5297         {header: "Market Cap.", width: 100, sortable: true},
5298         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299         {header: "Employees", width: 100, sortable: true, resizable: false}
5300  ]);
5301  </code></pre>
5302  * <p>
5303  
5304  * The config options listed for this class are options which may appear in each
5305  * individual column definition.
5306  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5307  * @constructor
5308  * @param {Object} config An Array of column config objects. See this class's
5309  * config objects for details.
5310 */
5311 Roo.grid.ColumnModel = function(config){
5312         /**
5313      * The config passed into the constructor
5314      */
5315     this.config = config;
5316     this.lookup = {};
5317
5318     // if no id, create one
5319     // if the column does not have a dataIndex mapping,
5320     // map it to the order it is in the config
5321     for(var i = 0, len = config.length; i < len; i++){
5322         var c = config[i];
5323         if(typeof c.dataIndex == "undefined"){
5324             c.dataIndex = i;
5325         }
5326         if(typeof c.renderer == "string"){
5327             c.renderer = Roo.util.Format[c.renderer];
5328         }
5329         if(typeof c.id == "undefined"){
5330             c.id = Roo.id();
5331         }
5332         if(c.editor && c.editor.xtype){
5333             c.editor  = Roo.factory(c.editor, Roo.grid);
5334         }
5335         if(c.editor && c.editor.isFormField){
5336             c.editor = new Roo.grid.GridEditor(c.editor);
5337         }
5338         this.lookup[c.id] = c;
5339     }
5340
5341     /**
5342      * The width of columns which have no width specified (defaults to 100)
5343      * @type Number
5344      */
5345     this.defaultWidth = 100;
5346
5347     /**
5348      * Default sortable of columns which have no sortable specified (defaults to false)
5349      * @type Boolean
5350      */
5351     this.defaultSortable = false;
5352
5353     this.addEvents({
5354         /**
5355              * @event widthchange
5356              * Fires when the width of a column changes.
5357              * @param {ColumnModel} this
5358              * @param {Number} columnIndex The column index
5359              * @param {Number} newWidth The new width
5360              */
5361             "widthchange": true,
5362         /**
5363              * @event headerchange
5364              * Fires when the text of a header changes.
5365              * @param {ColumnModel} this
5366              * @param {Number} columnIndex The column index
5367              * @param {Number} newText The new header text
5368              */
5369             "headerchange": true,
5370         /**
5371              * @event hiddenchange
5372              * Fires when a column is hidden or "unhidden".
5373              * @param {ColumnModel} this
5374              * @param {Number} columnIndex The column index
5375              * @param {Boolean} hidden true if hidden, false otherwise
5376              */
5377             "hiddenchange": true,
5378             /**
5379          * @event columnmoved
5380          * Fires when a column is moved.
5381          * @param {ColumnModel} this
5382          * @param {Number} oldIndex
5383          * @param {Number} newIndex
5384          */
5385         "columnmoved" : true,
5386         /**
5387          * @event columlockchange
5388          * Fires when a column's locked state is changed
5389          * @param {ColumnModel} this
5390          * @param {Number} colIndex
5391          * @param {Boolean} locked true if locked
5392          */
5393         "columnlockchange" : true
5394     });
5395     Roo.grid.ColumnModel.superclass.constructor.call(this);
5396 };
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5398     /**
5399      * @cfg {String} header The header text to display in the Grid view.
5400      */
5401     /**
5402      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404      * specified, the column's index is used as an index into the Record's data Array.
5405      */
5406     /**
5407      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5409      */
5410     /**
5411      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412      * Defaults to the value of the {@link #defaultSortable} property.
5413      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5414      */
5415     /**
5416      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5417      */
5418     /**
5419      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5420      */
5421     /**
5422      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5423      */
5424     /**
5425      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5426      */
5427     /**
5428      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5432      */
5433        /**
5434      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5435      */
5436     /**
5437      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5438      */
5439     /**
5440      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5441      */
5442     /**
5443      * @cfg {String} cursor (Optional)
5444      */
5445     /**
5446      * @cfg {String} tooltip (Optional)
5447      */
5448     /**
5449      * @cfg {Number} xs (Optional)
5450      */
5451     /**
5452      * @cfg {Number} sm (Optional)
5453      */
5454     /**
5455      * @cfg {Number} md (Optional)
5456      */
5457     /**
5458      * @cfg {Number} lg (Optional)
5459      */
5460     /**
5461      * Returns the id of the column at the specified index.
5462      * @param {Number} index The column index
5463      * @return {String} the id
5464      */
5465     getColumnId : function(index){
5466         return this.config[index].id;
5467     },
5468
5469     /**
5470      * Returns the column for a specified id.
5471      * @param {String} id The column id
5472      * @return {Object} the column
5473      */
5474     getColumnById : function(id){
5475         return this.lookup[id];
5476     },
5477
5478     
5479     /**
5480      * Returns the column for a specified dataIndex.
5481      * @param {String} dataIndex The column dataIndex
5482      * @return {Object|Boolean} the column or false if not found
5483      */
5484     getColumnByDataIndex: function(dataIndex){
5485         var index = this.findColumnIndex(dataIndex);
5486         return index > -1 ? this.config[index] : false;
5487     },
5488     
5489     /**
5490      * Returns the index for a specified column id.
5491      * @param {String} id The column id
5492      * @return {Number} the index, or -1 if not found
5493      */
5494     getIndexById : function(id){
5495         for(var i = 0, len = this.config.length; i < len; i++){
5496             if(this.config[i].id == id){
5497                 return i;
5498             }
5499         }
5500         return -1;
5501     },
5502     
5503     /**
5504      * Returns the index for a specified column dataIndex.
5505      * @param {String} dataIndex The column dataIndex
5506      * @return {Number} the index, or -1 if not found
5507      */
5508     
5509     findColumnIndex : function(dataIndex){
5510         for(var i = 0, len = this.config.length; i < len; i++){
5511             if(this.config[i].dataIndex == dataIndex){
5512                 return i;
5513             }
5514         }
5515         return -1;
5516     },
5517     
5518     
5519     moveColumn : function(oldIndex, newIndex){
5520         var c = this.config[oldIndex];
5521         this.config.splice(oldIndex, 1);
5522         this.config.splice(newIndex, 0, c);
5523         this.dataMap = null;
5524         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5525     },
5526
5527     isLocked : function(colIndex){
5528         return this.config[colIndex].locked === true;
5529     },
5530
5531     setLocked : function(colIndex, value, suppressEvent){
5532         if(this.isLocked(colIndex) == value){
5533             return;
5534         }
5535         this.config[colIndex].locked = value;
5536         if(!suppressEvent){
5537             this.fireEvent("columnlockchange", this, colIndex, value);
5538         }
5539     },
5540
5541     getTotalLockedWidth : function(){
5542         var totalWidth = 0;
5543         for(var i = 0; i < this.config.length; i++){
5544             if(this.isLocked(i) && !this.isHidden(i)){
5545                 this.totalWidth += this.getColumnWidth(i);
5546             }
5547         }
5548         return totalWidth;
5549     },
5550
5551     getLockedCount : function(){
5552         for(var i = 0, len = this.config.length; i < len; i++){
5553             if(!this.isLocked(i)){
5554                 return i;
5555             }
5556         }
5557         
5558         return this.config.length;
5559     },
5560
5561     /**
5562      * Returns the number of columns.
5563      * @return {Number}
5564      */
5565     getColumnCount : function(visibleOnly){
5566         if(visibleOnly === true){
5567             var c = 0;
5568             for(var i = 0, len = this.config.length; i < len; i++){
5569                 if(!this.isHidden(i)){
5570                     c++;
5571                 }
5572             }
5573             return c;
5574         }
5575         return this.config.length;
5576     },
5577
5578     /**
5579      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580      * @param {Function} fn
5581      * @param {Object} scope (optional)
5582      * @return {Array} result
5583      */
5584     getColumnsBy : function(fn, scope){
5585         var r = [];
5586         for(var i = 0, len = this.config.length; i < len; i++){
5587             var c = this.config[i];
5588             if(fn.call(scope||this, c, i) === true){
5589                 r[r.length] = c;
5590             }
5591         }
5592         return r;
5593     },
5594
5595     /**
5596      * Returns true if the specified column is sortable.
5597      * @param {Number} col The column index
5598      * @return {Boolean}
5599      */
5600     isSortable : function(col){
5601         if(typeof this.config[col].sortable == "undefined"){
5602             return this.defaultSortable;
5603         }
5604         return this.config[col].sortable;
5605     },
5606
5607     /**
5608      * Returns the rendering (formatting) function defined for the column.
5609      * @param {Number} col The column index.
5610      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5611      */
5612     getRenderer : function(col){
5613         if(!this.config[col].renderer){
5614             return Roo.grid.ColumnModel.defaultRenderer;
5615         }
5616         return this.config[col].renderer;
5617     },
5618
5619     /**
5620      * Sets the rendering (formatting) function for a column.
5621      * @param {Number} col The column index
5622      * @param {Function} fn The function to use to process the cell's raw data
5623      * to return HTML markup for the grid view. The render function is called with
5624      * the following parameters:<ul>
5625      * <li>Data value.</li>
5626      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627      * <li>css A CSS style string to apply to the table cell.</li>
5628      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630      * <li>Row index</li>
5631      * <li>Column index</li>
5632      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5633      */
5634     setRenderer : function(col, fn){
5635         this.config[col].renderer = fn;
5636     },
5637
5638     /**
5639      * Returns the width for the specified column.
5640      * @param {Number} col The column index
5641      * @return {Number}
5642      */
5643     getColumnWidth : function(col){
5644         return this.config[col].width * 1 || this.defaultWidth;
5645     },
5646
5647     /**
5648      * Sets the width for a column.
5649      * @param {Number} col The column index
5650      * @param {Number} width The new width
5651      */
5652     setColumnWidth : function(col, width, suppressEvent){
5653         this.config[col].width = width;
5654         this.totalWidth = null;
5655         if(!suppressEvent){
5656              this.fireEvent("widthchange", this, col, width);
5657         }
5658     },
5659
5660     /**
5661      * Returns the total width of all columns.
5662      * @param {Boolean} includeHidden True to include hidden column widths
5663      * @return {Number}
5664      */
5665     getTotalWidth : function(includeHidden){
5666         if(!this.totalWidth){
5667             this.totalWidth = 0;
5668             for(var i = 0, len = this.config.length; i < len; i++){
5669                 if(includeHidden || !this.isHidden(i)){
5670                     this.totalWidth += this.getColumnWidth(i);
5671                 }
5672             }
5673         }
5674         return this.totalWidth;
5675     },
5676
5677     /**
5678      * Returns the header for the specified column.
5679      * @param {Number} col The column index
5680      * @return {String}
5681      */
5682     getColumnHeader : function(col){
5683         return this.config[col].header;
5684     },
5685
5686     /**
5687      * Sets the header for a column.
5688      * @param {Number} col The column index
5689      * @param {String} header The new header
5690      */
5691     setColumnHeader : function(col, header){
5692         this.config[col].header = header;
5693         this.fireEvent("headerchange", this, col, header);
5694     },
5695
5696     /**
5697      * Returns the tooltip for the specified column.
5698      * @param {Number} col The column index
5699      * @return {String}
5700      */
5701     getColumnTooltip : function(col){
5702             return this.config[col].tooltip;
5703     },
5704     /**
5705      * Sets the tooltip for a column.
5706      * @param {Number} col The column index
5707      * @param {String} tooltip The new tooltip
5708      */
5709     setColumnTooltip : function(col, tooltip){
5710             this.config[col].tooltip = tooltip;
5711     },
5712
5713     /**
5714      * Returns the dataIndex for the specified column.
5715      * @param {Number} col The column index
5716      * @return {Number}
5717      */
5718     getDataIndex : function(col){
5719         return this.config[col].dataIndex;
5720     },
5721
5722     /**
5723      * Sets the dataIndex for a column.
5724      * @param {Number} col The column index
5725      * @param {Number} dataIndex The new dataIndex
5726      */
5727     setDataIndex : function(col, dataIndex){
5728         this.config[col].dataIndex = dataIndex;
5729     },
5730
5731     
5732     
5733     /**
5734      * Returns true if the cell is editable.
5735      * @param {Number} colIndex The column index
5736      * @param {Number} rowIndex The row index - this is nto actually used..?
5737      * @return {Boolean}
5738      */
5739     isCellEditable : function(colIndex, rowIndex){
5740         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5741     },
5742
5743     /**
5744      * Returns the editor defined for the cell/column.
5745      * return false or null to disable editing.
5746      * @param {Number} colIndex The column index
5747      * @param {Number} rowIndex The row index
5748      * @return {Object}
5749      */
5750     getCellEditor : function(colIndex, rowIndex){
5751         return this.config[colIndex].editor;
5752     },
5753
5754     /**
5755      * Sets if a column is editable.
5756      * @param {Number} col The column index
5757      * @param {Boolean} editable True if the column is editable
5758      */
5759     setEditable : function(col, editable){
5760         this.config[col].editable = editable;
5761     },
5762
5763
5764     /**
5765      * Returns true if the column is hidden.
5766      * @param {Number} colIndex The column index
5767      * @return {Boolean}
5768      */
5769     isHidden : function(colIndex){
5770         return this.config[colIndex].hidden;
5771     },
5772
5773
5774     /**
5775      * Returns true if the column width cannot be changed
5776      */
5777     isFixed : function(colIndex){
5778         return this.config[colIndex].fixed;
5779     },
5780
5781     /**
5782      * Returns true if the column can be resized
5783      * @return {Boolean}
5784      */
5785     isResizable : function(colIndex){
5786         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5787     },
5788     /**
5789      * Sets if a column is hidden.
5790      * @param {Number} colIndex The column index
5791      * @param {Boolean} hidden True if the column is hidden
5792      */
5793     setHidden : function(colIndex, hidden){
5794         this.config[colIndex].hidden = hidden;
5795         this.totalWidth = null;
5796         this.fireEvent("hiddenchange", this, colIndex, hidden);
5797     },
5798
5799     /**
5800      * Sets the editor for a column.
5801      * @param {Number} col The column index
5802      * @param {Object} editor The editor object
5803      */
5804     setEditor : function(col, editor){
5805         this.config[col].editor = editor;
5806     }
5807 });
5808
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5810 {
5811     if(typeof value == "object") {
5812         return value;
5813     }
5814         if(typeof value == "string" && value.length < 1){
5815             return "&#160;";
5816         }
5817     
5818         return String.format("{0}", value);
5819 };
5820
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5823 /*
5824  * Based on:
5825  * Ext JS Library 1.1.1
5826  * Copyright(c) 2006-2007, Ext JS, LLC.
5827  *
5828  * Originally Released Under LGPL - original licence link has changed is not relivant.
5829  *
5830  * Fork - LGPL
5831  * <script type="text/javascript">
5832  */
5833  
5834 /**
5835  * @class Roo.LoadMask
5836  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5837  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5839  * element's UpdateManager load indicator and will be destroyed after the initial load.
5840  * @constructor
5841  * Create a new LoadMask
5842  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843  * @param {Object} config The config object
5844  */
5845 Roo.LoadMask = function(el, config){
5846     this.el = Roo.get(el);
5847     Roo.apply(this, config);
5848     if(this.store){
5849         this.store.on('beforeload', this.onBeforeLoad, this);
5850         this.store.on('load', this.onLoad, this);
5851         this.store.on('loadexception', this.onLoadException, this);
5852         this.removeMask = false;
5853     }else{
5854         var um = this.el.getUpdateManager();
5855         um.showLoadIndicator = false; // disable the default indicator
5856         um.on('beforeupdate', this.onBeforeLoad, this);
5857         um.on('update', this.onLoad, this);
5858         um.on('failure', this.onLoad, this);
5859         this.removeMask = true;
5860     }
5861 };
5862
5863 Roo.LoadMask.prototype = {
5864     /**
5865      * @cfg {Boolean} removeMask
5866      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5868      */
5869     /**
5870      * @cfg {String} msg
5871      * The text to display in a centered loading message box (defaults to 'Loading...')
5872      */
5873     msg : 'Loading...',
5874     /**
5875      * @cfg {String} msgCls
5876      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5877      */
5878     msgCls : 'x-mask-loading',
5879
5880     /**
5881      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5882      * @type Boolean
5883      */
5884     disabled: false,
5885
5886     /**
5887      * Disables the mask to prevent it from being displayed
5888      */
5889     disable : function(){
5890        this.disabled = true;
5891     },
5892
5893     /**
5894      * Enables the mask so that it can be displayed
5895      */
5896     enable : function(){
5897         this.disabled = false;
5898     },
5899     
5900     onLoadException : function()
5901     {
5902         Roo.log(arguments);
5903         
5904         if (typeof(arguments[3]) != 'undefined') {
5905             Roo.MessageBox.alert("Error loading",arguments[3]);
5906         } 
5907         /*
5908         try {
5909             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5911             }   
5912         } catch(e) {
5913             
5914         }
5915         */
5916     
5917         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5918     },
5919     // private
5920     onLoad : function()
5921     {
5922         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923     },
5924
5925     // private
5926     onBeforeLoad : function(){
5927         if(!this.disabled){
5928             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5929         }
5930     },
5931
5932     // private
5933     destroy : function(){
5934         if(this.store){
5935             this.store.un('beforeload', this.onBeforeLoad, this);
5936             this.store.un('load', this.onLoad, this);
5937             this.store.un('loadexception', this.onLoadException, this);
5938         }else{
5939             var um = this.el.getUpdateManager();
5940             um.un('beforeupdate', this.onBeforeLoad, this);
5941             um.un('update', this.onLoad, this);
5942             um.un('failure', this.onLoad, this);
5943         }
5944     }
5945 };/*
5946  * - LGPL
5947  *
5948  * table
5949  * 
5950  */
5951
5952 /**
5953  * @class Roo.bootstrap.Table
5954  * @extends Roo.bootstrap.Component
5955  * Bootstrap Table class
5956  * @cfg {String} cls table class
5957  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958  * @cfg {String} bgcolor Specifies the background color for a table
5959  * @cfg {Number} border Specifies whether the table cells should have borders or not
5960  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961  * @cfg {Number} cellspacing Specifies the space between cells
5962  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964  * @cfg {String} sortable Specifies that the table should be sortable
5965  * @cfg {String} summary Specifies a summary of the content of a table
5966  * @cfg {Number} width Specifies the width of a table
5967  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5968  * 
5969  * @cfg {boolean} striped Should the rows be alternative striped
5970  * @cfg {boolean} bordered Add borders to the table
5971  * @cfg {boolean} hover Add hover highlighting
5972  * @cfg {boolean} condensed Format condensed
5973  * @cfg {boolean} responsive Format condensed
5974  * @cfg {Boolean} loadMask (true|false) default false
5975  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977  * @cfg {Boolean} rowSelection (true|false) default false
5978  * @cfg {Boolean} cellSelection (true|false) default false
5979  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5981  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5982  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5983  
5984  * 
5985  * @constructor
5986  * Create a new Table
5987  * @param {Object} config The config object
5988  */
5989
5990 Roo.bootstrap.Table = function(config){
5991     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5992     
5993   
5994     
5995     // BC...
5996     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6000     
6001     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6002     if (this.sm) {
6003         this.sm.grid = this;
6004         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005         this.sm = this.selModel;
6006         this.sm.xmodule = this.xmodule || false;
6007     }
6008     
6009     if (this.cm && typeof(this.cm.config) == 'undefined') {
6010         this.colModel = new Roo.grid.ColumnModel(this.cm);
6011         this.cm = this.colModel;
6012         this.cm.xmodule = this.xmodule || false;
6013     }
6014     if (this.store) {
6015         this.store= Roo.factory(this.store, Roo.data);
6016         this.ds = this.store;
6017         this.ds.xmodule = this.xmodule || false;
6018          
6019     }
6020     if (this.footer && this.store) {
6021         this.footer.dataSource = this.ds;
6022         this.footer = Roo.factory(this.footer);
6023     }
6024     
6025     /** @private */
6026     this.addEvents({
6027         /**
6028          * @event cellclick
6029          * Fires when a cell is clicked
6030          * @param {Roo.bootstrap.Table} this
6031          * @param {Roo.Element} el
6032          * @param {Number} rowIndex
6033          * @param {Number} columnIndex
6034          * @param {Roo.EventObject} e
6035          */
6036         "cellclick" : true,
6037         /**
6038          * @event celldblclick
6039          * Fires when a cell is double clicked
6040          * @param {Roo.bootstrap.Table} this
6041          * @param {Roo.Element} el
6042          * @param {Number} rowIndex
6043          * @param {Number} columnIndex
6044          * @param {Roo.EventObject} e
6045          */
6046         "celldblclick" : true,
6047         /**
6048          * @event rowclick
6049          * Fires when a row is clicked
6050          * @param {Roo.bootstrap.Table} this
6051          * @param {Roo.Element} el
6052          * @param {Number} rowIndex
6053          * @param {Roo.EventObject} e
6054          */
6055         "rowclick" : true,
6056         /**
6057          * @event rowdblclick
6058          * Fires when a row is double clicked
6059          * @param {Roo.bootstrap.Table} this
6060          * @param {Roo.Element} el
6061          * @param {Number} rowIndex
6062          * @param {Roo.EventObject} e
6063          */
6064         "rowdblclick" : true,
6065         /**
6066          * @event mouseover
6067          * Fires when a mouseover occur
6068          * @param {Roo.bootstrap.Table} this
6069          * @param {Roo.Element} el
6070          * @param {Number} rowIndex
6071          * @param {Number} columnIndex
6072          * @param {Roo.EventObject} e
6073          */
6074         "mouseover" : true,
6075         /**
6076          * @event mouseout
6077          * Fires when a mouseout occur
6078          * @param {Roo.bootstrap.Table} this
6079          * @param {Roo.Element} el
6080          * @param {Number} rowIndex
6081          * @param {Number} columnIndex
6082          * @param {Roo.EventObject} e
6083          */
6084         "mouseout" : true,
6085         /**
6086          * @event rowclass
6087          * Fires when a row is rendered, so you can change add a style to it.
6088          * @param {Roo.bootstrap.Table} this
6089          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6090          */
6091         'rowclass' : true,
6092           /**
6093          * @event rowsrendered
6094          * Fires when all the  rows have been rendered
6095          * @param {Roo.bootstrap.Table} this
6096          */
6097         'rowsrendered' : true,
6098         /**
6099          * @event contextmenu
6100          * The raw contextmenu event for the entire grid.
6101          * @param {Roo.EventObject} e
6102          */
6103         "contextmenu" : true,
6104         /**
6105          * @event rowcontextmenu
6106          * Fires when a row is right clicked
6107          * @param {Roo.bootstrap.Table} this
6108          * @param {Number} rowIndex
6109          * @param {Roo.EventObject} e
6110          */
6111         "rowcontextmenu" : true,
6112         /**
6113          * @event cellcontextmenu
6114          * Fires when a cell is right clicked
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Number} rowIndex
6117          * @param {Number} cellIndex
6118          * @param {Roo.EventObject} e
6119          */
6120          "cellcontextmenu" : true,
6121          /**
6122          * @event headercontextmenu
6123          * Fires when a header is right clicked
6124          * @param {Roo.bootstrap.Table} this
6125          * @param {Number} columnIndex
6126          * @param {Roo.EventObject} e
6127          */
6128         "headercontextmenu" : true
6129     });
6130 };
6131
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6133     
6134     cls: false,
6135     align: false,
6136     bgcolor: false,
6137     border: false,
6138     cellpadding: false,
6139     cellspacing: false,
6140     frame: false,
6141     rules: false,
6142     sortable: false,
6143     summary: false,
6144     width: false,
6145     striped : false,
6146     scrollBody : false,
6147     bordered: false,
6148     hover:  false,
6149     condensed : false,
6150     responsive : false,
6151     sm : false,
6152     cm : false,
6153     store : false,
6154     loadMask : false,
6155     footerShow : true,
6156     headerShow : true,
6157   
6158     rowSelection : false,
6159     cellSelection : false,
6160     layout : false,
6161     
6162     // Roo.Element - the tbody
6163     mainBody: false,
6164     // Roo.Element - thead element
6165     mainHead: false,
6166     
6167     container: false, // used by gridpanel...
6168     
6169     lazyLoad : false,
6170     
6171     CSS : Roo.util.CSS,
6172     
6173     auto_hide_footer : false,
6174     
6175     getAutoCreate : function()
6176     {
6177         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'table',
6181             cls : 'table',
6182             cn : []
6183         };
6184         if (this.scrollBody) {
6185             cfg.cls += ' table-body-fixed';
6186         }    
6187         if (this.striped) {
6188             cfg.cls += ' table-striped';
6189         }
6190         
6191         if (this.hover) {
6192             cfg.cls += ' table-hover';
6193         }
6194         if (this.bordered) {
6195             cfg.cls += ' table-bordered';
6196         }
6197         if (this.condensed) {
6198             cfg.cls += ' table-condensed';
6199         }
6200         if (this.responsive) {
6201             cfg.cls += ' table-responsive';
6202         }
6203         
6204         if (this.cls) {
6205             cfg.cls+=  ' ' +this.cls;
6206         }
6207         
6208         // this lot should be simplifed...
6209         var _t = this;
6210         var cp = [
6211             'align',
6212             'bgcolor',
6213             'border',
6214             'cellpadding',
6215             'cellspacing',
6216             'frame',
6217             'rules',
6218             'sortable',
6219             'summary',
6220             'width'
6221         ].forEach(function(k) {
6222             if (_t[k]) {
6223                 cfg[k] = _t[k];
6224             }
6225         });
6226         
6227         
6228         if (this.layout) {
6229             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6230         }
6231         
6232         if(this.store || this.cm){
6233             if(this.headerShow){
6234                 cfg.cn.push(this.renderHeader());
6235             }
6236             
6237             cfg.cn.push(this.renderBody());
6238             
6239             if(this.footerShow){
6240                 cfg.cn.push(this.renderFooter());
6241             }
6242             // where does this come from?
6243             //cfg.cls+=  ' TableGrid';
6244         }
6245         
6246         return { cn : [ cfg ] };
6247     },
6248     
6249     initEvents : function()
6250     {   
6251         if(!this.store || !this.cm){
6252             return;
6253         }
6254         if (this.selModel) {
6255             this.selModel.initEvents();
6256         }
6257         
6258         
6259         //Roo.log('initEvents with ds!!!!');
6260         
6261         this.mainBody = this.el.select('tbody', true).first();
6262         this.mainHead = this.el.select('thead', true).first();
6263         this.mainFoot = this.el.select('tfoot', true).first();
6264         
6265         
6266         
6267         var _this = this;
6268         
6269         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270             e.on('click', _this.sort, _this);
6271         });
6272         
6273         this.mainBody.on("click", this.onClick, this);
6274         this.mainBody.on("dblclick", this.onDblClick, this);
6275         
6276         // why is this done????? = it breaks dialogs??
6277         //this.parent().el.setStyle('position', 'relative');
6278         
6279         
6280         if (this.footer) {
6281             this.footer.parentId = this.id;
6282             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6283             
6284             if(this.lazyLoad){
6285                 this.el.select('tfoot tr td').first().addClass('hide');
6286             }
6287         } 
6288         
6289         if(this.loadMask) {
6290             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6291         }
6292         
6293         this.store.on('load', this.onLoad, this);
6294         this.store.on('beforeload', this.onBeforeLoad, this);
6295         this.store.on('update', this.onUpdate, this);
6296         this.store.on('add', this.onAdd, this);
6297         this.store.on("clear", this.clear, this);
6298         
6299         this.el.on("contextmenu", this.onContextMenu, this);
6300         
6301         this.mainBody.on('scroll', this.onBodyScroll, this);
6302         
6303         this.cm.on("headerchange", this.onHeaderChange, this);
6304         
6305         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6306         
6307     },
6308     
6309     onContextMenu : function(e, t)
6310     {
6311         this.processEvent("contextmenu", e);
6312     },
6313     
6314     processEvent : function(name, e)
6315     {
6316         if (name != 'touchstart' ) {
6317             this.fireEvent(name, e);    
6318         }
6319         
6320         var t = e.getTarget();
6321         
6322         var cell = Roo.get(t);
6323         
6324         if(!cell){
6325             return;
6326         }
6327         
6328         if(cell.findParent('tfoot', false, true)){
6329             return;
6330         }
6331         
6332         if(cell.findParent('thead', false, true)){
6333             
6334             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335                 cell = Roo.get(t).findParent('th', false, true);
6336                 if (!cell) {
6337                     Roo.log("failed to find th in thead?");
6338                     Roo.log(e.getTarget());
6339                     return;
6340                 }
6341             }
6342             
6343             var cellIndex = cell.dom.cellIndex;
6344             
6345             var ename = name == 'touchstart' ? 'click' : name;
6346             this.fireEvent("header" + ename, this, cellIndex, e);
6347             
6348             return;
6349         }
6350         
6351         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352             cell = Roo.get(t).findParent('td', false, true);
6353             if (!cell) {
6354                 Roo.log("failed to find th in tbody?");
6355                 Roo.log(e.getTarget());
6356                 return;
6357             }
6358         }
6359         
6360         var row = cell.findParent('tr', false, true);
6361         var cellIndex = cell.dom.cellIndex;
6362         var rowIndex = row.dom.rowIndex - 1;
6363         
6364         if(row !== false){
6365             
6366             this.fireEvent("row" + name, this, rowIndex, e);
6367             
6368             if(cell !== false){
6369             
6370                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6371             }
6372         }
6373         
6374     },
6375     
6376     onMouseover : function(e, el)
6377     {
6378         var cell = Roo.get(el);
6379         
6380         if(!cell){
6381             return;
6382         }
6383         
6384         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385             cell = cell.findParent('td', false, true);
6386         }
6387         
6388         var row = cell.findParent('tr', false, true);
6389         var cellIndex = cell.dom.cellIndex;
6390         var rowIndex = row.dom.rowIndex - 1; // start from 0
6391         
6392         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6393         
6394     },
6395     
6396     onMouseout : function(e, el)
6397     {
6398         var cell = Roo.get(el);
6399         
6400         if(!cell){
6401             return;
6402         }
6403         
6404         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405             cell = cell.findParent('td', false, true);
6406         }
6407         
6408         var row = cell.findParent('tr', false, true);
6409         var cellIndex = cell.dom.cellIndex;
6410         var rowIndex = row.dom.rowIndex - 1; // start from 0
6411         
6412         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6413         
6414     },
6415     
6416     onClick : function(e, el)
6417     {
6418         var cell = Roo.get(el);
6419         
6420         if(!cell || (!this.cellSelection && !this.rowSelection)){
6421             return;
6422         }
6423         
6424         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425             cell = cell.findParent('td', false, true);
6426         }
6427         
6428         if(!cell || typeof(cell) == 'undefined'){
6429             return;
6430         }
6431         
6432         var row = cell.findParent('tr', false, true);
6433         
6434         if(!row || typeof(row) == 'undefined'){
6435             return;
6436         }
6437         
6438         var cellIndex = cell.dom.cellIndex;
6439         var rowIndex = this.getRowIndex(row);
6440         
6441         // why??? - should these not be based on SelectionModel?
6442         if(this.cellSelection){
6443             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6444         }
6445         
6446         if(this.rowSelection){
6447             this.fireEvent('rowclick', this, row, rowIndex, e);
6448         }
6449         
6450         
6451     },
6452         
6453     onDblClick : function(e,el)
6454     {
6455         var cell = Roo.get(el);
6456         
6457         if(!cell || (!this.cellSelection && !this.rowSelection)){
6458             return;
6459         }
6460         
6461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462             cell = cell.findParent('td', false, true);
6463         }
6464         
6465         if(!cell || typeof(cell) == 'undefined'){
6466             return;
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         
6471         if(!row || typeof(row) == 'undefined'){
6472             return;
6473         }
6474         
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = this.getRowIndex(row);
6477         
6478         if(this.cellSelection){
6479             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6480         }
6481         
6482         if(this.rowSelection){
6483             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6484         }
6485     },
6486     
6487     sort : function(e,el)
6488     {
6489         var col = Roo.get(el);
6490         
6491         if(!col.hasClass('sortable')){
6492             return;
6493         }
6494         
6495         var sort = col.attr('sort');
6496         var dir = 'ASC';
6497         
6498         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6499             dir = 'DESC';
6500         }
6501         
6502         this.store.sortInfo = {field : sort, direction : dir};
6503         
6504         if (this.footer) {
6505             Roo.log("calling footer first");
6506             this.footer.onClick('first');
6507         } else {
6508         
6509             this.store.load({ params : { start : 0 } });
6510         }
6511     },
6512     
6513     renderHeader : function()
6514     {
6515         var header = {
6516             tag: 'thead',
6517             cn : []
6518         };
6519         
6520         var cm = this.cm;
6521         this.totalWidth = 0;
6522         
6523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6524             
6525             var config = cm.config[i];
6526             
6527             var c = {
6528                 tag: 'th',
6529                 cls : 'x-hcol-' + i,
6530                 style : '',
6531                 html: cm.getColumnHeader(i)
6532             };
6533             
6534             var hh = '';
6535             
6536             if(typeof(config.sortable) != 'undefined' && config.sortable){
6537                 c.cls = 'sortable';
6538                 c.html = '<i class="glyphicon"></i>' + c.html;
6539             }
6540             
6541             if(typeof(config.lgHeader) != 'undefined'){
6542                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6543             }
6544             
6545             if(typeof(config.mdHeader) != 'undefined'){
6546                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6547             }
6548             
6549             if(typeof(config.smHeader) != 'undefined'){
6550                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6551             }
6552             
6553             if(typeof(config.xsHeader) != 'undefined'){
6554                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6555             }
6556             
6557             if(hh.length){
6558                 c.html = hh;
6559             }
6560             
6561             if(typeof(config.tooltip) != 'undefined'){
6562                 c.tooltip = config.tooltip;
6563             }
6564             
6565             if(typeof(config.colspan) != 'undefined'){
6566                 c.colspan = config.colspan;
6567             }
6568             
6569             if(typeof(config.hidden) != 'undefined' && config.hidden){
6570                 c.style += ' display:none;';
6571             }
6572             
6573             if(typeof(config.dataIndex) != 'undefined'){
6574                 c.sort = config.dataIndex;
6575             }
6576             
6577            
6578             
6579             if(typeof(config.align) != 'undefined' && config.align.length){
6580                 c.style += ' text-align:' + config.align + ';';
6581             }
6582             
6583             if(typeof(config.width) != 'undefined'){
6584                 c.style += ' width:' + config.width + 'px;';
6585                 this.totalWidth += config.width;
6586             } else {
6587                 this.totalWidth += 100; // assume minimum of 100 per column?
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     c.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 c.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608             
6609             header.cn.push(c)
6610         }
6611         
6612         return header;
6613     },
6614     
6615     renderBody : function()
6616     {
6617         var body = {
6618             tag: 'tbody',
6619             cn : [
6620                 {
6621                     tag: 'tr',
6622                     cn : [
6623                         {
6624                             tag : 'td',
6625                             colspan :  this.cm.getColumnCount()
6626                         }
6627                     ]
6628                 }
6629             ]
6630         };
6631         
6632         return body;
6633     },
6634     
6635     renderFooter : function()
6636     {
6637         var footer = {
6638             tag: 'tfoot',
6639             cn : [
6640                 {
6641                     tag: 'tr',
6642                     cn : [
6643                         {
6644                             tag : 'td',
6645                             colspan :  this.cm.getColumnCount()
6646                         }
6647                     ]
6648                 }
6649             ]
6650         };
6651         
6652         return footer;
6653     },
6654     
6655     
6656     
6657     onLoad : function()
6658     {
6659 //        Roo.log('ds onload');
6660         this.clear();
6661         
6662         var _this = this;
6663         var cm = this.cm;
6664         var ds = this.store;
6665         
6666         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668             if (_this.store.sortInfo) {
6669                     
6670                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6672                 }
6673                 
6674                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6676                 }
6677             }
6678         });
6679         
6680         var tbody =  this.mainBody;
6681               
6682         if(ds.getCount() > 0){
6683             ds.data.each(function(d,rowIndex){
6684                 var row =  this.renderRow(cm, ds, rowIndex);
6685                 
6686                 tbody.createChild(row);
6687                 
6688                 var _this = this;
6689                 
6690                 if(row.cellObjects.length){
6691                     Roo.each(row.cellObjects, function(r){
6692                         _this.renderCellObject(r);
6693                     })
6694                 }
6695                 
6696             }, this);
6697         }
6698         
6699         var tfoot = this.el.select('tfoot', true).first();
6700         
6701         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6702             
6703             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6704             
6705             var total = this.ds.getTotalCount();
6706             
6707             if(this.footer.pageSize < total){
6708                 this.mainFoot.show();
6709             }
6710         }
6711         
6712         Roo.each(this.el.select('tbody td', true).elements, function(e){
6713             e.on('mouseover', _this.onMouseover, _this);
6714         });
6715         
6716         Roo.each(this.el.select('tbody td', true).elements, function(e){
6717             e.on('mouseout', _this.onMouseout, _this);
6718         });
6719         this.fireEvent('rowsrendered', this);
6720         
6721         this.autoSize();
6722     },
6723     
6724     
6725     onUpdate : function(ds,record)
6726     {
6727         this.refreshRow(record);
6728         this.autoSize();
6729     },
6730     
6731     onRemove : function(ds, record, index, isUpdate){
6732         if(isUpdate !== true){
6733             this.fireEvent("beforerowremoved", this, index, record);
6734         }
6735         var bt = this.mainBody.dom;
6736         
6737         var rows = this.el.select('tbody > tr', true).elements;
6738         
6739         if(typeof(rows[index]) != 'undefined'){
6740             bt.removeChild(rows[index].dom);
6741         }
6742         
6743 //        if(bt.rows[index]){
6744 //            bt.removeChild(bt.rows[index]);
6745 //        }
6746         
6747         if(isUpdate !== true){
6748             //this.stripeRows(index);
6749             //this.syncRowHeights(index, index);
6750             //this.layout();
6751             this.fireEvent("rowremoved", this, index, record);
6752         }
6753     },
6754     
6755     onAdd : function(ds, records, rowIndex)
6756     {
6757         //Roo.log('on Add called');
6758         // - note this does not handle multiple adding very well..
6759         var bt = this.mainBody.dom;
6760         for (var i =0 ; i < records.length;i++) {
6761             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762             //Roo.log(records[i]);
6763             //Roo.log(this.store.getAt(rowIndex+i));
6764             this.insertRow(this.store, rowIndex + i, false);
6765             return;
6766         }
6767         
6768     },
6769     
6770     
6771     refreshRow : function(record){
6772         var ds = this.store, index;
6773         if(typeof record == 'number'){
6774             index = record;
6775             record = ds.getAt(index);
6776         }else{
6777             index = ds.indexOf(record);
6778         }
6779         this.insertRow(ds, index, true);
6780         this.autoSize();
6781         this.onRemove(ds, record, index+1, true);
6782         this.autoSize();
6783         //this.syncRowHeights(index, index);
6784         //this.layout();
6785         this.fireEvent("rowupdated", this, index, record);
6786     },
6787     
6788     insertRow : function(dm, rowIndex, isUpdate){
6789         
6790         if(!isUpdate){
6791             this.fireEvent("beforerowsinserted", this, rowIndex);
6792         }
6793             //var s = this.getScrollState();
6794         var row = this.renderRow(this.cm, this.store, rowIndex);
6795         // insert before rowIndex..
6796         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6797         
6798         var _this = this;
6799                 
6800         if(row.cellObjects.length){
6801             Roo.each(row.cellObjects, function(r){
6802                 _this.renderCellObject(r);
6803             })
6804         }
6805             
6806         if(!isUpdate){
6807             this.fireEvent("rowsinserted", this, rowIndex);
6808             //this.syncRowHeights(firstRow, lastRow);
6809             //this.stripeRows(firstRow);
6810             //this.layout();
6811         }
6812         
6813     },
6814     
6815     
6816     getRowDom : function(rowIndex)
6817     {
6818         var rows = this.el.select('tbody > tr', true).elements;
6819         
6820         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6821         
6822     },
6823     // returns the object tree for a tr..
6824   
6825     
6826     renderRow : function(cm, ds, rowIndex) 
6827     {
6828         var d = ds.getAt(rowIndex);
6829         
6830         var row = {
6831             tag : 'tr',
6832             cls : 'x-row-' + rowIndex,
6833             cn : []
6834         };
6835             
6836         var cellObjects = [];
6837         
6838         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839             var config = cm.config[i];
6840             
6841             var renderer = cm.getRenderer(i);
6842             var value = '';
6843             var id = false;
6844             
6845             if(typeof(renderer) !== 'undefined'){
6846                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6847             }
6848             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849             // and are rendered into the cells after the row is rendered - using the id for the element.
6850             
6851             if(typeof(value) === 'object'){
6852                 id = Roo.id();
6853                 cellObjects.push({
6854                     container : id,
6855                     cfg : value 
6856                 })
6857             }
6858             
6859             var rowcfg = {
6860                 record: d,
6861                 rowIndex : rowIndex,
6862                 colIndex : i,
6863                 rowClass : ''
6864             };
6865
6866             this.fireEvent('rowclass', this, rowcfg);
6867             
6868             var td = {
6869                 tag: 'td',
6870                 cls : rowcfg.rowClass + ' x-col-' + i,
6871                 style: '',
6872                 html: (typeof(value) === 'object') ? '' : value
6873             };
6874             
6875             if (id) {
6876                 td.id = id;
6877             }
6878             
6879             if(typeof(config.colspan) != 'undefined'){
6880                 td.colspan = config.colspan;
6881             }
6882             
6883             if(typeof(config.hidden) != 'undefined' && config.hidden){
6884                 td.style += ' display:none;';
6885             }
6886             
6887             if(typeof(config.align) != 'undefined' && config.align.length){
6888                 td.style += ' text-align:' + config.align + ';';
6889             }
6890             if(typeof(config.valign) != 'undefined' && config.valign.length){
6891                 td.style += ' vertical-align:' + config.valign + ';';
6892             }
6893             
6894             if(typeof(config.width) != 'undefined'){
6895                 td.style += ' width:' +  config.width + 'px;';
6896             }
6897             
6898             if(typeof(config.cursor) != 'undefined'){
6899                 td.style += ' cursor:' +  config.cursor + ';';
6900             }
6901             
6902             if(typeof(config.cls) != 'undefined'){
6903                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6904             }
6905             
6906             ['xs','sm','md','lg'].map(function(size){
6907                 
6908                 if(typeof(config[size]) == 'undefined'){
6909                     return;
6910                 }
6911                 
6912                 if (!config[size]) { // 0 = hidden
6913                     td.cls += ' hidden-' + size;
6914                     return;
6915                 }
6916                 
6917                 td.cls += ' col-' + size + '-' + config[size];
6918
6919             });
6920             
6921             row.cn.push(td);
6922            
6923         }
6924         
6925         row.cellObjects = cellObjects;
6926         
6927         return row;
6928           
6929     },
6930     
6931     
6932     
6933     onBeforeLoad : function()
6934     {
6935         
6936     },
6937      /**
6938      * Remove all rows
6939      */
6940     clear : function()
6941     {
6942         this.el.select('tbody', true).first().dom.innerHTML = '';
6943     },
6944     /**
6945      * Show or hide a row.
6946      * @param {Number} rowIndex to show or hide
6947      * @param {Boolean} state hide
6948      */
6949     setRowVisibility : function(rowIndex, state)
6950     {
6951         var bt = this.mainBody.dom;
6952         
6953         var rows = this.el.select('tbody > tr', true).elements;
6954         
6955         if(typeof(rows[rowIndex]) == 'undefined'){
6956             return;
6957         }
6958         rows[rowIndex].dom.style.display = state ? '' : 'none';
6959     },
6960     
6961     
6962     getSelectionModel : function(){
6963         if(!this.selModel){
6964             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6965         }
6966         return this.selModel;
6967     },
6968     /*
6969      * Render the Roo.bootstrap object from renderder
6970      */
6971     renderCellObject : function(r)
6972     {
6973         var _this = this;
6974         
6975         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6976         
6977         var t = r.cfg.render(r.container);
6978         
6979         if(r.cfg.cn){
6980             Roo.each(r.cfg.cn, function(c){
6981                 var child = {
6982                     container: t.getChildContainer(),
6983                     cfg: c
6984                 };
6985                 _this.renderCellObject(child);
6986             })
6987         }
6988     },
6989     
6990     getRowIndex : function(row)
6991     {
6992         var rowIndex = -1;
6993         
6994         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6995             if(el != row){
6996                 return;
6997             }
6998             
6999             rowIndex = index;
7000         });
7001         
7002         return rowIndex;
7003     },
7004      /**
7005      * Returns the grid's underlying element = used by panel.Grid
7006      * @return {Element} The element
7007      */
7008     getGridEl : function(){
7009         return this.el;
7010     },
7011      /**
7012      * Forces a resize - used by panel.Grid
7013      * @return {Element} The element
7014      */
7015     autoSize : function()
7016     {
7017         //var ctr = Roo.get(this.container.dom.parentElement);
7018         var ctr = Roo.get(this.el.dom);
7019         
7020         var thd = this.getGridEl().select('thead',true).first();
7021         var tbd = this.getGridEl().select('tbody', true).first();
7022         var tfd = this.getGridEl().select('tfoot', true).first();
7023         
7024         var cw = ctr.getWidth();
7025         
7026         if (tbd) {
7027             
7028             tbd.setSize(ctr.getWidth(),
7029                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7030             );
7031             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7032             cw -= barsize;
7033         }
7034         cw = Math.max(cw, this.totalWidth);
7035         this.getGridEl().select('tr',true).setWidth(cw);
7036         // resize 'expandable coloumn?
7037         
7038         return; // we doe not have a view in this design..
7039         
7040     },
7041     onBodyScroll: function()
7042     {
7043         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7044         if(this.mainHead){
7045             this.mainHead.setStyle({
7046                 'position' : 'relative',
7047                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7048             });
7049         }
7050         
7051         if(this.lazyLoad){
7052             
7053             var scrollHeight = this.mainBody.dom.scrollHeight;
7054             
7055             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7056             
7057             var height = this.mainBody.getHeight();
7058             
7059             if(scrollHeight - height == scrollTop) {
7060                 
7061                 var total = this.ds.getTotalCount();
7062                 
7063                 if(this.footer.cursor + this.footer.pageSize < total){
7064                     
7065                     this.footer.ds.load({
7066                         params : {
7067                             start : this.footer.cursor + this.footer.pageSize,
7068                             limit : this.footer.pageSize
7069                         },
7070                         add : true
7071                     });
7072                 }
7073             }
7074             
7075         }
7076     },
7077     
7078     onHeaderChange : function()
7079     {
7080         var header = this.renderHeader();
7081         var table = this.el.select('table', true).first();
7082         
7083         this.mainHead.remove();
7084         this.mainHead = table.createChild(header, this.mainBody, false);
7085     },
7086     
7087     onHiddenChange : function(colModel, colIndex, hidden)
7088     {
7089         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7091         
7092         this.CSS.updateRule(thSelector, "display", "");
7093         this.CSS.updateRule(tdSelector, "display", "");
7094         
7095         if(hidden){
7096             this.CSS.updateRule(thSelector, "display", "none");
7097             this.CSS.updateRule(tdSelector, "display", "none");
7098         }
7099         
7100         this.onHeaderChange();
7101         this.onLoad();
7102     },
7103     
7104     setColumnWidth: function(col_index, width)
7105     {
7106         // width = "md-2 xs-2..."
7107         if(!this.colModel.config[col_index]) {
7108             return;
7109         }
7110         
7111         var w = width.split(" ");
7112         
7113         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7114         
7115         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7116         
7117         
7118         for(var j = 0; j < w.length; j++) {
7119             
7120             if(!w[j]) {
7121                 continue;
7122             }
7123             
7124             var size_cls = w[j].split("-");
7125             
7126             if(!Number.isInteger(size_cls[1] * 1)) {
7127                 continue;
7128             }
7129             
7130             if(!this.colModel.config[col_index][size_cls[0]]) {
7131                 continue;
7132             }
7133             
7134             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7135                 continue;
7136             }
7137             
7138             h_row[0].classList.replace(
7139                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7140                 "col-"+size_cls[0]+"-"+size_cls[1]
7141             );
7142             
7143             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7144         }
7145         
7146         for(var i = 0; i < rows.length; i++) {
7147             
7148             for(var j = 0; j < w.length; j++) {
7149                 
7150                 var size_cls = w[j].split("-");
7151                 
7152                 if(!Number.isInteger(size_cls[1] * 1)) {
7153                     continue;
7154                 }
7155                 
7156                 if(!this.colModel.config[col_index][size_cls[0]]) {
7157                     continue;
7158                 }
7159                 
7160                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7161                     continue;
7162                 }
7163                 
7164                 Roo.log('not replacing??');
7165                 
7166                 rows[i].classList.replace(
7167                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7168                     "col-"+size_cls[0]+"-"+size_cls[1]
7169                 );
7170             }
7171         }
7172     }
7173 });
7174
7175  
7176
7177  /*
7178  * - LGPL
7179  *
7180  * table cell
7181  * 
7182  */
7183
7184 /**
7185  * @class Roo.bootstrap.TableCell
7186  * @extends Roo.bootstrap.Component
7187  * Bootstrap TableCell class
7188  * @cfg {String} html cell contain text
7189  * @cfg {String} cls cell class
7190  * @cfg {String} tag cell tag (td|th) default td
7191  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7192  * @cfg {String} align Aligns the content in a cell
7193  * @cfg {String} axis Categorizes cells
7194  * @cfg {String} bgcolor Specifies the background color of a cell
7195  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7196  * @cfg {Number} colspan Specifies the number of columns a cell should span
7197  * @cfg {String} headers Specifies one or more header cells a cell is related to
7198  * @cfg {Number} height Sets the height of a cell
7199  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7200  * @cfg {Number} rowspan Sets the number of rows a cell should span
7201  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7202  * @cfg {String} valign Vertical aligns the content in a cell
7203  * @cfg {Number} width Specifies the width of a cell
7204  * 
7205  * @constructor
7206  * Create a new TableCell
7207  * @param {Object} config The config object
7208  */
7209
7210 Roo.bootstrap.TableCell = function(config){
7211     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7212 };
7213
7214 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7215     
7216     html: false,
7217     cls: false,
7218     tag: false,
7219     abbr: false,
7220     align: false,
7221     axis: false,
7222     bgcolor: false,
7223     charoff: false,
7224     colspan: false,
7225     headers: false,
7226     height: false,
7227     nowrap: false,
7228     rowspan: false,
7229     scope: false,
7230     valign: false,
7231     width: false,
7232     
7233     
7234     getAutoCreate : function(){
7235         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7236         
7237         cfg = {
7238             tag: 'td'
7239         };
7240         
7241         if(this.tag){
7242             cfg.tag = this.tag;
7243         }
7244         
7245         if (this.html) {
7246             cfg.html=this.html
7247         }
7248         if (this.cls) {
7249             cfg.cls=this.cls
7250         }
7251         if (this.abbr) {
7252             cfg.abbr=this.abbr
7253         }
7254         if (this.align) {
7255             cfg.align=this.align
7256         }
7257         if (this.axis) {
7258             cfg.axis=this.axis
7259         }
7260         if (this.bgcolor) {
7261             cfg.bgcolor=this.bgcolor
7262         }
7263         if (this.charoff) {
7264             cfg.charoff=this.charoff
7265         }
7266         if (this.colspan) {
7267             cfg.colspan=this.colspan
7268         }
7269         if (this.headers) {
7270             cfg.headers=this.headers
7271         }
7272         if (this.height) {
7273             cfg.height=this.height
7274         }
7275         if (this.nowrap) {
7276             cfg.nowrap=this.nowrap
7277         }
7278         if (this.rowspan) {
7279             cfg.rowspan=this.rowspan
7280         }
7281         if (this.scope) {
7282             cfg.scope=this.scope
7283         }
7284         if (this.valign) {
7285             cfg.valign=this.valign
7286         }
7287         if (this.width) {
7288             cfg.width=this.width
7289         }
7290         
7291         
7292         return cfg;
7293     }
7294    
7295 });
7296
7297  
7298
7299  /*
7300  * - LGPL
7301  *
7302  * table row
7303  * 
7304  */
7305
7306 /**
7307  * @class Roo.bootstrap.TableRow
7308  * @extends Roo.bootstrap.Component
7309  * Bootstrap TableRow class
7310  * @cfg {String} cls row class
7311  * @cfg {String} align Aligns the content in a table row
7312  * @cfg {String} bgcolor Specifies a background color for a table row
7313  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7314  * @cfg {String} valign Vertical aligns the content in a table row
7315  * 
7316  * @constructor
7317  * Create a new TableRow
7318  * @param {Object} config The config object
7319  */
7320
7321 Roo.bootstrap.TableRow = function(config){
7322     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7323 };
7324
7325 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7326     
7327     cls: false,
7328     align: false,
7329     bgcolor: false,
7330     charoff: false,
7331     valign: false,
7332     
7333     getAutoCreate : function(){
7334         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7335         
7336         cfg = {
7337             tag: 'tr'
7338         };
7339             
7340         if(this.cls){
7341             cfg.cls = this.cls;
7342         }
7343         if(this.align){
7344             cfg.align = this.align;
7345         }
7346         if(this.bgcolor){
7347             cfg.bgcolor = this.bgcolor;
7348         }
7349         if(this.charoff){
7350             cfg.charoff = this.charoff;
7351         }
7352         if(this.valign){
7353             cfg.valign = this.valign;
7354         }
7355         
7356         return cfg;
7357     }
7358    
7359 });
7360
7361  
7362
7363  /*
7364  * - LGPL
7365  *
7366  * table body
7367  * 
7368  */
7369
7370 /**
7371  * @class Roo.bootstrap.TableBody
7372  * @extends Roo.bootstrap.Component
7373  * Bootstrap TableBody class
7374  * @cfg {String} cls element class
7375  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7376  * @cfg {String} align Aligns the content inside the element
7377  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7378  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7379  * 
7380  * @constructor
7381  * Create a new TableBody
7382  * @param {Object} config The config object
7383  */
7384
7385 Roo.bootstrap.TableBody = function(config){
7386     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7387 };
7388
7389 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7390     
7391     cls: false,
7392     tag: false,
7393     align: false,
7394     charoff: false,
7395     valign: false,
7396     
7397     getAutoCreate : function(){
7398         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7399         
7400         cfg = {
7401             tag: 'tbody'
7402         };
7403             
7404         if (this.cls) {
7405             cfg.cls=this.cls
7406         }
7407         if(this.tag){
7408             cfg.tag = this.tag;
7409         }
7410         
7411         if(this.align){
7412             cfg.align = this.align;
7413         }
7414         if(this.charoff){
7415             cfg.charoff = this.charoff;
7416         }
7417         if(this.valign){
7418             cfg.valign = this.valign;
7419         }
7420         
7421         return cfg;
7422     }
7423     
7424     
7425 //    initEvents : function()
7426 //    {
7427 //        
7428 //        if(!this.store){
7429 //            return;
7430 //        }
7431 //        
7432 //        this.store = Roo.factory(this.store, Roo.data);
7433 //        this.store.on('load', this.onLoad, this);
7434 //        
7435 //        this.store.load();
7436 //        
7437 //    },
7438 //    
7439 //    onLoad: function () 
7440 //    {   
7441 //        this.fireEvent('load', this);
7442 //    }
7443 //    
7444 //   
7445 });
7446
7447  
7448
7449  /*
7450  * Based on:
7451  * Ext JS Library 1.1.1
7452  * Copyright(c) 2006-2007, Ext JS, LLC.
7453  *
7454  * Originally Released Under LGPL - original licence link has changed is not relivant.
7455  *
7456  * Fork - LGPL
7457  * <script type="text/javascript">
7458  */
7459
7460 // as we use this in bootstrap.
7461 Roo.namespace('Roo.form');
7462  /**
7463  * @class Roo.form.Action
7464  * Internal Class used to handle form actions
7465  * @constructor
7466  * @param {Roo.form.BasicForm} el The form element or its id
7467  * @param {Object} config Configuration options
7468  */
7469
7470  
7471  
7472 // define the action interface
7473 Roo.form.Action = function(form, options){
7474     this.form = form;
7475     this.options = options || {};
7476 };
7477 /**
7478  * Client Validation Failed
7479  * @const 
7480  */
7481 Roo.form.Action.CLIENT_INVALID = 'client';
7482 /**
7483  * Server Validation Failed
7484  * @const 
7485  */
7486 Roo.form.Action.SERVER_INVALID = 'server';
7487  /**
7488  * Connect to Server Failed
7489  * @const 
7490  */
7491 Roo.form.Action.CONNECT_FAILURE = 'connect';
7492 /**
7493  * Reading Data from Server Failed
7494  * @const 
7495  */
7496 Roo.form.Action.LOAD_FAILURE = 'load';
7497
7498 Roo.form.Action.prototype = {
7499     type : 'default',
7500     failureType : undefined,
7501     response : undefined,
7502     result : undefined,
7503
7504     // interface method
7505     run : function(options){
7506
7507     },
7508
7509     // interface method
7510     success : function(response){
7511
7512     },
7513
7514     // interface method
7515     handleResponse : function(response){
7516
7517     },
7518
7519     // default connection failure
7520     failure : function(response){
7521         
7522         this.response = response;
7523         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7524         this.form.afterAction(this, false);
7525     },
7526
7527     processResponse : function(response){
7528         this.response = response;
7529         if(!response.responseText){
7530             return true;
7531         }
7532         this.result = this.handleResponse(response);
7533         return this.result;
7534     },
7535
7536     // utility functions used internally
7537     getUrl : function(appendParams){
7538         var url = this.options.url || this.form.url || this.form.el.dom.action;
7539         if(appendParams){
7540             var p = this.getParams();
7541             if(p){
7542                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7543             }
7544         }
7545         return url;
7546     },
7547
7548     getMethod : function(){
7549         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7550     },
7551
7552     getParams : function(){
7553         var bp = this.form.baseParams;
7554         var p = this.options.params;
7555         if(p){
7556             if(typeof p == "object"){
7557                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7558             }else if(typeof p == 'string' && bp){
7559                 p += '&' + Roo.urlEncode(bp);
7560             }
7561         }else if(bp){
7562             p = Roo.urlEncode(bp);
7563         }
7564         return p;
7565     },
7566
7567     createCallback : function(){
7568         return {
7569             success: this.success,
7570             failure: this.failure,
7571             scope: this,
7572             timeout: (this.form.timeout*1000),
7573             upload: this.form.fileUpload ? this.success : undefined
7574         };
7575     }
7576 };
7577
7578 Roo.form.Action.Submit = function(form, options){
7579     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7580 };
7581
7582 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7583     type : 'submit',
7584
7585     haveProgress : false,
7586     uploadComplete : false,
7587     
7588     // uploadProgress indicator.
7589     uploadProgress : function()
7590     {
7591         if (!this.form.progressUrl) {
7592             return;
7593         }
7594         
7595         if (!this.haveProgress) {
7596             Roo.MessageBox.progress("Uploading", "Uploading");
7597         }
7598         if (this.uploadComplete) {
7599            Roo.MessageBox.hide();
7600            return;
7601         }
7602         
7603         this.haveProgress = true;
7604    
7605         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7606         
7607         var c = new Roo.data.Connection();
7608         c.request({
7609             url : this.form.progressUrl,
7610             params: {
7611                 id : uid
7612             },
7613             method: 'GET',
7614             success : function(req){
7615                //console.log(data);
7616                 var rdata = false;
7617                 var edata;
7618                 try  {
7619                    rdata = Roo.decode(req.responseText)
7620                 } catch (e) {
7621                     Roo.log("Invalid data from server..");
7622                     Roo.log(edata);
7623                     return;
7624                 }
7625                 if (!rdata || !rdata.success) {
7626                     Roo.log(rdata);
7627                     Roo.MessageBox.alert(Roo.encode(rdata));
7628                     return;
7629                 }
7630                 var data = rdata.data;
7631                 
7632                 if (this.uploadComplete) {
7633                    Roo.MessageBox.hide();
7634                    return;
7635                 }
7636                    
7637                 if (data){
7638                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7639                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7640                     );
7641                 }
7642                 this.uploadProgress.defer(2000,this);
7643             },
7644        
7645             failure: function(data) {
7646                 Roo.log('progress url failed ');
7647                 Roo.log(data);
7648             },
7649             scope : this
7650         });
7651            
7652     },
7653     
7654     
7655     run : function()
7656     {
7657         // run get Values on the form, so it syncs any secondary forms.
7658         this.form.getValues();
7659         
7660         var o = this.options;
7661         var method = this.getMethod();
7662         var isPost = method == 'POST';
7663         if(o.clientValidation === false || this.form.isValid()){
7664             
7665             if (this.form.progressUrl) {
7666                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7667                     (new Date() * 1) + '' + Math.random());
7668                     
7669             } 
7670             
7671             
7672             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7673                 form:this.form.el.dom,
7674                 url:this.getUrl(!isPost),
7675                 method: method,
7676                 params:isPost ? this.getParams() : null,
7677                 isUpload: this.form.fileUpload
7678             }));
7679             
7680             this.uploadProgress();
7681
7682         }else if (o.clientValidation !== false){ // client validation failed
7683             this.failureType = Roo.form.Action.CLIENT_INVALID;
7684             this.form.afterAction(this, false);
7685         }
7686     },
7687
7688     success : function(response)
7689     {
7690         this.uploadComplete= true;
7691         if (this.haveProgress) {
7692             Roo.MessageBox.hide();
7693         }
7694         
7695         
7696         var result = this.processResponse(response);
7697         if(result === true || result.success){
7698             this.form.afterAction(this, true);
7699             return;
7700         }
7701         if(result.errors){
7702             this.form.markInvalid(result.errors);
7703             this.failureType = Roo.form.Action.SERVER_INVALID;
7704         }
7705         this.form.afterAction(this, false);
7706     },
7707     failure : function(response)
7708     {
7709         this.uploadComplete= true;
7710         if (this.haveProgress) {
7711             Roo.MessageBox.hide();
7712         }
7713         
7714         this.response = response;
7715         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7716         this.form.afterAction(this, false);
7717     },
7718     
7719     handleResponse : function(response){
7720         if(this.form.errorReader){
7721             var rs = this.form.errorReader.read(response);
7722             var errors = [];
7723             if(rs.records){
7724                 for(var i = 0, len = rs.records.length; i < len; i++) {
7725                     var r = rs.records[i];
7726                     errors[i] = r.data;
7727                 }
7728             }
7729             if(errors.length < 1){
7730                 errors = null;
7731             }
7732             return {
7733                 success : rs.success,
7734                 errors : errors
7735             };
7736         }
7737         var ret = false;
7738         try {
7739             ret = Roo.decode(response.responseText);
7740         } catch (e) {
7741             ret = {
7742                 success: false,
7743                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7744                 errors : []
7745             };
7746         }
7747         return ret;
7748         
7749     }
7750 });
7751
7752
7753 Roo.form.Action.Load = function(form, options){
7754     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7755     this.reader = this.form.reader;
7756 };
7757
7758 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7759     type : 'load',
7760
7761     run : function(){
7762         
7763         Roo.Ajax.request(Roo.apply(
7764                 this.createCallback(), {
7765                     method:this.getMethod(),
7766                     url:this.getUrl(false),
7767                     params:this.getParams()
7768         }));
7769     },
7770
7771     success : function(response){
7772         
7773         var result = this.processResponse(response);
7774         if(result === true || !result.success || !result.data){
7775             this.failureType = Roo.form.Action.LOAD_FAILURE;
7776             this.form.afterAction(this, false);
7777             return;
7778         }
7779         this.form.clearInvalid();
7780         this.form.setValues(result.data);
7781         this.form.afterAction(this, true);
7782     },
7783
7784     handleResponse : function(response){
7785         if(this.form.reader){
7786             var rs = this.form.reader.read(response);
7787             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7788             return {
7789                 success : rs.success,
7790                 data : data
7791             };
7792         }
7793         return Roo.decode(response.responseText);
7794     }
7795 });
7796
7797 Roo.form.Action.ACTION_TYPES = {
7798     'load' : Roo.form.Action.Load,
7799     'submit' : Roo.form.Action.Submit
7800 };/*
7801  * - LGPL
7802  *
7803  * form
7804  *
7805  */
7806
7807 /**
7808  * @class Roo.bootstrap.Form
7809  * @extends Roo.bootstrap.Component
7810  * Bootstrap Form class
7811  * @cfg {String} method  GET | POST (default POST)
7812  * @cfg {String} labelAlign top | left (default top)
7813  * @cfg {String} align left  | right - for navbars
7814  * @cfg {Boolean} loadMask load mask when submit (default true)
7815
7816  *
7817  * @constructor
7818  * Create a new Form
7819  * @param {Object} config The config object
7820  */
7821
7822
7823 Roo.bootstrap.Form = function(config){
7824     
7825     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7826     
7827     Roo.bootstrap.Form.popover.apply();
7828     
7829     this.addEvents({
7830         /**
7831          * @event clientvalidation
7832          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7833          * @param {Form} this
7834          * @param {Boolean} valid true if the form has passed client-side validation
7835          */
7836         clientvalidation: true,
7837         /**
7838          * @event beforeaction
7839          * Fires before any action is performed. Return false to cancel the action.
7840          * @param {Form} this
7841          * @param {Action} action The action to be performed
7842          */
7843         beforeaction: true,
7844         /**
7845          * @event actionfailed
7846          * Fires when an action fails.
7847          * @param {Form} this
7848          * @param {Action} action The action that failed
7849          */
7850         actionfailed : true,
7851         /**
7852          * @event actioncomplete
7853          * Fires when an action is completed.
7854          * @param {Form} this
7855          * @param {Action} action The action that completed
7856          */
7857         actioncomplete : true
7858     });
7859 };
7860
7861 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7862
7863      /**
7864      * @cfg {String} method
7865      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7866      */
7867     method : 'POST',
7868     /**
7869      * @cfg {String} url
7870      * The URL to use for form actions if one isn't supplied in the action options.
7871      */
7872     /**
7873      * @cfg {Boolean} fileUpload
7874      * Set to true if this form is a file upload.
7875      */
7876
7877     /**
7878      * @cfg {Object} baseParams
7879      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7880      */
7881
7882     /**
7883      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7884      */
7885     timeout: 30,
7886     /**
7887      * @cfg {Sting} align (left|right) for navbar forms
7888      */
7889     align : 'left',
7890
7891     // private
7892     activeAction : null,
7893
7894     /**
7895      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7896      * element by passing it or its id or mask the form itself by passing in true.
7897      * @type Mixed
7898      */
7899     waitMsgTarget : false,
7900
7901     loadMask : true,
7902     
7903     /**
7904      * @cfg {Boolean} errorMask (true|false) default false
7905      */
7906     errorMask : false,
7907     
7908     /**
7909      * @cfg {Number} maskOffset Default 100
7910      */
7911     maskOffset : 100,
7912     
7913     /**
7914      * @cfg {Boolean} maskBody
7915      */
7916     maskBody : false,
7917
7918     getAutoCreate : function(){
7919
7920         var cfg = {
7921             tag: 'form',
7922             method : this.method || 'POST',
7923             id : this.id || Roo.id(),
7924             cls : ''
7925         };
7926         if (this.parent().xtype.match(/^Nav/)) {
7927             cfg.cls = 'navbar-form navbar-' + this.align;
7928
7929         }
7930
7931         if (this.labelAlign == 'left' ) {
7932             cfg.cls += ' form-horizontal';
7933         }
7934
7935
7936         return cfg;
7937     },
7938     initEvents : function()
7939     {
7940         this.el.on('submit', this.onSubmit, this);
7941         // this was added as random key presses on the form where triggering form submit.
7942         this.el.on('keypress', function(e) {
7943             if (e.getCharCode() != 13) {
7944                 return true;
7945             }
7946             // we might need to allow it for textareas.. and some other items.
7947             // check e.getTarget().
7948
7949             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7950                 return true;
7951             }
7952
7953             Roo.log("keypress blocked");
7954
7955             e.preventDefault();
7956             return false;
7957         });
7958         
7959     },
7960     // private
7961     onSubmit : function(e){
7962         e.stopEvent();
7963     },
7964
7965      /**
7966      * Returns true if client-side validation on the form is successful.
7967      * @return Boolean
7968      */
7969     isValid : function(){
7970         var items = this.getItems();
7971         var valid = true;
7972         var target = false;
7973         
7974         items.each(function(f){
7975             
7976             if(f.validate()){
7977                 return;
7978             }
7979             
7980             Roo.log('invalid field: ' + f.name);
7981             
7982             valid = false;
7983
7984             if(!target && f.el.isVisible(true)){
7985                 target = f;
7986             }
7987            
7988         });
7989         
7990         if(this.errorMask && !valid){
7991             Roo.bootstrap.Form.popover.mask(this, target);
7992         }
7993         
7994         return valid;
7995     },
7996     
7997     /**
7998      * Returns true if any fields in this form have changed since their original load.
7999      * @return Boolean
8000      */
8001     isDirty : function(){
8002         var dirty = false;
8003         var items = this.getItems();
8004         items.each(function(f){
8005            if(f.isDirty()){
8006                dirty = true;
8007                return false;
8008            }
8009            return true;
8010         });
8011         return dirty;
8012     },
8013      /**
8014      * Performs a predefined action (submit or load) or custom actions you define on this form.
8015      * @param {String} actionName The name of the action type
8016      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8017      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8018      * accept other config options):
8019      * <pre>
8020 Property          Type             Description
8021 ----------------  ---------------  ----------------------------------------------------------------------------------
8022 url               String           The url for the action (defaults to the form's url)
8023 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8024 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8025 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8026                                    validate the form on the client (defaults to false)
8027      * </pre>
8028      * @return {BasicForm} this
8029      */
8030     doAction : function(action, options){
8031         if(typeof action == 'string'){
8032             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8033         }
8034         if(this.fireEvent('beforeaction', this, action) !== false){
8035             this.beforeAction(action);
8036             action.run.defer(100, action);
8037         }
8038         return this;
8039     },
8040
8041     // private
8042     beforeAction : function(action){
8043         var o = action.options;
8044         
8045         if(this.loadMask){
8046             
8047             if(this.maskBody){
8048                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8049             } else {
8050                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8051             }
8052         }
8053         // not really supported yet.. ??
8054
8055         //if(this.waitMsgTarget === true){
8056         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8057         //}else if(this.waitMsgTarget){
8058         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8059         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8060         //}else {
8061         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8062        // }
8063
8064     },
8065
8066     // private
8067     afterAction : function(action, success){
8068         this.activeAction = null;
8069         var o = action.options;
8070
8071         if(this.loadMask){
8072             
8073             if(this.maskBody){
8074                 Roo.get(document.body).unmask();
8075             } else {
8076                 this.el.unmask();
8077             }
8078         }
8079         
8080         //if(this.waitMsgTarget === true){
8081 //            this.el.unmask();
8082         //}else if(this.waitMsgTarget){
8083         //    this.waitMsgTarget.unmask();
8084         //}else{
8085         //    Roo.MessageBox.updateProgress(1);
8086         //    Roo.MessageBox.hide();
8087        // }
8088         //
8089         if(success){
8090             if(o.reset){
8091                 this.reset();
8092             }
8093             Roo.callback(o.success, o.scope, [this, action]);
8094             this.fireEvent('actioncomplete', this, action);
8095
8096         }else{
8097
8098             // failure condition..
8099             // we have a scenario where updates need confirming.
8100             // eg. if a locking scenario exists..
8101             // we look for { errors : { needs_confirm : true }} in the response.
8102             if (
8103                 (typeof(action.result) != 'undefined')  &&
8104                 (typeof(action.result.errors) != 'undefined')  &&
8105                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8106            ){
8107                 var _t = this;
8108                 Roo.log("not supported yet");
8109                  /*
8110
8111                 Roo.MessageBox.confirm(
8112                     "Change requires confirmation",
8113                     action.result.errorMsg,
8114                     function(r) {
8115                         if (r != 'yes') {
8116                             return;
8117                         }
8118                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8119                     }
8120
8121                 );
8122                 */
8123
8124
8125                 return;
8126             }
8127
8128             Roo.callback(o.failure, o.scope, [this, action]);
8129             // show an error message if no failed handler is set..
8130             if (!this.hasListener('actionfailed')) {
8131                 Roo.log("need to add dialog support");
8132                 /*
8133                 Roo.MessageBox.alert("Error",
8134                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8135                         action.result.errorMsg :
8136                         "Saving Failed, please check your entries or try again"
8137                 );
8138                 */
8139             }
8140
8141             this.fireEvent('actionfailed', this, action);
8142         }
8143
8144     },
8145     /**
8146      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8147      * @param {String} id The value to search for
8148      * @return Field
8149      */
8150     findField : function(id){
8151         var items = this.getItems();
8152         var field = items.get(id);
8153         if(!field){
8154              items.each(function(f){
8155                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8156                     field = f;
8157                     return false;
8158                 }
8159                 return true;
8160             });
8161         }
8162         return field || null;
8163     },
8164      /**
8165      * Mark fields in this form invalid in bulk.
8166      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8167      * @return {BasicForm} this
8168      */
8169     markInvalid : function(errors){
8170         if(errors instanceof Array){
8171             for(var i = 0, len = errors.length; i < len; i++){
8172                 var fieldError = errors[i];
8173                 var f = this.findField(fieldError.id);
8174                 if(f){
8175                     f.markInvalid(fieldError.msg);
8176                 }
8177             }
8178         }else{
8179             var field, id;
8180             for(id in errors){
8181                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8182                     field.markInvalid(errors[id]);
8183                 }
8184             }
8185         }
8186         //Roo.each(this.childForms || [], function (f) {
8187         //    f.markInvalid(errors);
8188         //});
8189
8190         return this;
8191     },
8192
8193     /**
8194      * Set values for fields in this form in bulk.
8195      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8196      * @return {BasicForm} this
8197      */
8198     setValues : function(values){
8199         if(values instanceof Array){ // array of objects
8200             for(var i = 0, len = values.length; i < len; i++){
8201                 var v = values[i];
8202                 var f = this.findField(v.id);
8203                 if(f){
8204                     f.setValue(v.value);
8205                     if(this.trackResetOnLoad){
8206                         f.originalValue = f.getValue();
8207                     }
8208                 }
8209             }
8210         }else{ // object hash
8211             var field, id;
8212             for(id in values){
8213                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8214
8215                     if (field.setFromData &&
8216                         field.valueField &&
8217                         field.displayField &&
8218                         // combos' with local stores can
8219                         // be queried via setValue()
8220                         // to set their value..
8221                         (field.store && !field.store.isLocal)
8222                         ) {
8223                         // it's a combo
8224                         var sd = { };
8225                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8226                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8227                         field.setFromData(sd);
8228
8229                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8230                         
8231                         field.setFromData(values);
8232                         
8233                     } else {
8234                         field.setValue(values[id]);
8235                     }
8236
8237
8238                     if(this.trackResetOnLoad){
8239                         field.originalValue = field.getValue();
8240                     }
8241                 }
8242             }
8243         }
8244
8245         //Roo.each(this.childForms || [], function (f) {
8246         //    f.setValues(values);
8247         //});
8248
8249         return this;
8250     },
8251
8252     /**
8253      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8254      * they are returned as an array.
8255      * @param {Boolean} asString
8256      * @return {Object}
8257      */
8258     getValues : function(asString){
8259         //if (this.childForms) {
8260             // copy values from the child forms
8261         //    Roo.each(this.childForms, function (f) {
8262         //        this.setValues(f.getValues());
8263         //    }, this);
8264         //}
8265
8266
8267
8268         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8269         if(asString === true){
8270             return fs;
8271         }
8272         return Roo.urlDecode(fs);
8273     },
8274
8275     /**
8276      * Returns the fields in this form as an object with key/value pairs.
8277      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8278      * @return {Object}
8279      */
8280     getFieldValues : function(with_hidden)
8281     {
8282         var items = this.getItems();
8283         var ret = {};
8284         items.each(function(f){
8285             
8286             if (!f.getName()) {
8287                 return;
8288             }
8289             
8290             var v = f.getValue();
8291             
8292             if (f.inputType =='radio') {
8293                 if (typeof(ret[f.getName()]) == 'undefined') {
8294                     ret[f.getName()] = ''; // empty..
8295                 }
8296
8297                 if (!f.el.dom.checked) {
8298                     return;
8299
8300                 }
8301                 v = f.el.dom.value;
8302
8303             }
8304             
8305             if(f.xtype == 'MoneyField'){
8306                 ret[f.currencyName] = f.getCurrency();
8307             }
8308
8309             // not sure if this supported any more..
8310             if ((typeof(v) == 'object') && f.getRawValue) {
8311                 v = f.getRawValue() ; // dates..
8312             }
8313             // combo boxes where name != hiddenName...
8314             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8315                 ret[f.name] = f.getRawValue();
8316             }
8317             ret[f.getName()] = v;
8318         });
8319
8320         return ret;
8321     },
8322
8323     /**
8324      * Clears all invalid messages in this form.
8325      * @return {BasicForm} this
8326      */
8327     clearInvalid : function(){
8328         var items = this.getItems();
8329
8330         items.each(function(f){
8331            f.clearInvalid();
8332         });
8333
8334         return this;
8335     },
8336
8337     /**
8338      * Resets this form.
8339      * @return {BasicForm} this
8340      */
8341     reset : function(){
8342         var items = this.getItems();
8343         items.each(function(f){
8344             f.reset();
8345         });
8346
8347         Roo.each(this.childForms || [], function (f) {
8348             f.reset();
8349         });
8350
8351
8352         return this;
8353     },
8354     
8355     getItems : function()
8356     {
8357         var r=new Roo.util.MixedCollection(false, function(o){
8358             return o.id || (o.id = Roo.id());
8359         });
8360         var iter = function(el) {
8361             if (el.inputEl) {
8362                 r.add(el);
8363             }
8364             if (!el.items) {
8365                 return;
8366             }
8367             Roo.each(el.items,function(e) {
8368                 iter(e);
8369             });
8370         };
8371
8372         iter(this);
8373         return r;
8374     },
8375     
8376     hideFields : function(items)
8377     {
8378         Roo.each(items, function(i){
8379             
8380             var f = this.findField(i);
8381             
8382             if(!f){
8383                 return;
8384             }
8385             
8386             f.hide();
8387             
8388         }, this);
8389     },
8390     
8391     showFields : function(items)
8392     {
8393         Roo.each(items, function(i){
8394             
8395             var f = this.findField(i);
8396             
8397             if(!f){
8398                 return;
8399             }
8400             
8401             f.show();
8402             
8403         }, this);
8404     }
8405
8406 });
8407
8408 Roo.apply(Roo.bootstrap.Form, {
8409     
8410     popover : {
8411         
8412         padding : 5,
8413         
8414         isApplied : false,
8415         
8416         isMasked : false,
8417         
8418         form : false,
8419         
8420         target : false,
8421         
8422         toolTip : false,
8423         
8424         intervalID : false,
8425         
8426         maskEl : false,
8427         
8428         apply : function()
8429         {
8430             if(this.isApplied){
8431                 return;
8432             }
8433             
8434             this.maskEl = {
8435                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8436                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8437                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8438                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8439             };
8440             
8441             this.maskEl.top.enableDisplayMode("block");
8442             this.maskEl.left.enableDisplayMode("block");
8443             this.maskEl.bottom.enableDisplayMode("block");
8444             this.maskEl.right.enableDisplayMode("block");
8445             
8446             this.toolTip = new Roo.bootstrap.Tooltip({
8447                 cls : 'roo-form-error-popover',
8448                 alignment : {
8449                     'left' : ['r-l', [-2,0], 'right'],
8450                     'right' : ['l-r', [2,0], 'left'],
8451                     'bottom' : ['tl-bl', [0,2], 'top'],
8452                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8453                 }
8454             });
8455             
8456             this.toolTip.render(Roo.get(document.body));
8457
8458             this.toolTip.el.enableDisplayMode("block");
8459             
8460             Roo.get(document.body).on('click', function(){
8461                 this.unmask();
8462             }, this);
8463             
8464             Roo.get(document.body).on('touchstart', function(){
8465                 this.unmask();
8466             }, this);
8467             
8468             this.isApplied = true
8469         },
8470         
8471         mask : function(form, target)
8472         {
8473             this.form = form;
8474             
8475             this.target = target;
8476             
8477             if(!this.form.errorMask || !target.el){
8478                 return;
8479             }
8480             
8481             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8482             
8483             Roo.log(scrollable);
8484             
8485             var ot = this.target.el.calcOffsetsTo(scrollable);
8486             
8487             var scrollTo = ot[1] - this.form.maskOffset;
8488             
8489             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8490             
8491             scrollable.scrollTo('top', scrollTo);
8492             
8493             var box = this.target.el.getBox();
8494             Roo.log(box);
8495             var zIndex = Roo.bootstrap.Modal.zIndex++;
8496
8497             
8498             this.maskEl.top.setStyle('position', 'absolute');
8499             this.maskEl.top.setStyle('z-index', zIndex);
8500             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8501             this.maskEl.top.setLeft(0);
8502             this.maskEl.top.setTop(0);
8503             this.maskEl.top.show();
8504             
8505             this.maskEl.left.setStyle('position', 'absolute');
8506             this.maskEl.left.setStyle('z-index', zIndex);
8507             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8508             this.maskEl.left.setLeft(0);
8509             this.maskEl.left.setTop(box.y - this.padding);
8510             this.maskEl.left.show();
8511
8512             this.maskEl.bottom.setStyle('position', 'absolute');
8513             this.maskEl.bottom.setStyle('z-index', zIndex);
8514             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8515             this.maskEl.bottom.setLeft(0);
8516             this.maskEl.bottom.setTop(box.bottom + this.padding);
8517             this.maskEl.bottom.show();
8518
8519             this.maskEl.right.setStyle('position', 'absolute');
8520             this.maskEl.right.setStyle('z-index', zIndex);
8521             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8522             this.maskEl.right.setLeft(box.right + this.padding);
8523             this.maskEl.right.setTop(box.y - this.padding);
8524             this.maskEl.right.show();
8525
8526             this.toolTip.bindEl = this.target.el;
8527
8528             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8529
8530             var tip = this.target.blankText;
8531
8532             if(this.target.getValue() !== '' ) {
8533                 
8534                 if (this.target.invalidText.length) {
8535                     tip = this.target.invalidText;
8536                 } else if (this.target.regexText.length){
8537                     tip = this.target.regexText;
8538                 }
8539             }
8540
8541             this.toolTip.show(tip);
8542
8543             this.intervalID = window.setInterval(function() {
8544                 Roo.bootstrap.Form.popover.unmask();
8545             }, 10000);
8546
8547             window.onwheel = function(){ return false;};
8548             
8549             (function(){ this.isMasked = true; }).defer(500, this);
8550             
8551         },
8552         
8553         unmask : function()
8554         {
8555             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8556                 return;
8557             }
8558             
8559             this.maskEl.top.setStyle('position', 'absolute');
8560             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8561             this.maskEl.top.hide();
8562
8563             this.maskEl.left.setStyle('position', 'absolute');
8564             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8565             this.maskEl.left.hide();
8566
8567             this.maskEl.bottom.setStyle('position', 'absolute');
8568             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8569             this.maskEl.bottom.hide();
8570
8571             this.maskEl.right.setStyle('position', 'absolute');
8572             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8573             this.maskEl.right.hide();
8574             
8575             this.toolTip.hide();
8576             
8577             this.toolTip.el.hide();
8578             
8579             window.onwheel = function(){ return true;};
8580             
8581             if(this.intervalID){
8582                 window.clearInterval(this.intervalID);
8583                 this.intervalID = false;
8584             }
8585             
8586             this.isMasked = false;
8587             
8588         }
8589         
8590     }
8591     
8592 });
8593
8594 /*
8595  * Based on:
8596  * Ext JS Library 1.1.1
8597  * Copyright(c) 2006-2007, Ext JS, LLC.
8598  *
8599  * Originally Released Under LGPL - original licence link has changed is not relivant.
8600  *
8601  * Fork - LGPL
8602  * <script type="text/javascript">
8603  */
8604 /**
8605  * @class Roo.form.VTypes
8606  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8607  * @singleton
8608  */
8609 Roo.form.VTypes = function(){
8610     // closure these in so they are only created once.
8611     var alpha = /^[a-zA-Z_]+$/;
8612     var alphanum = /^[a-zA-Z0-9_]+$/;
8613     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8614     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8615
8616     // All these messages and functions are configurable
8617     return {
8618         /**
8619          * The function used to validate email addresses
8620          * @param {String} value The email address
8621          */
8622         'email' : function(v){
8623             return email.test(v);
8624         },
8625         /**
8626          * The error text to display when the email validation function returns false
8627          * @type String
8628          */
8629         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8630         /**
8631          * The keystroke filter mask to be applied on email input
8632          * @type RegExp
8633          */
8634         'emailMask' : /[a-z0-9_\.\-@]/i,
8635
8636         /**
8637          * The function used to validate URLs
8638          * @param {String} value The URL
8639          */
8640         'url' : function(v){
8641             return url.test(v);
8642         },
8643         /**
8644          * The error text to display when the url validation function returns false
8645          * @type String
8646          */
8647         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8648         
8649         /**
8650          * The function used to validate alpha values
8651          * @param {String} value The value
8652          */
8653         'alpha' : function(v){
8654             return alpha.test(v);
8655         },
8656         /**
8657          * The error text to display when the alpha validation function returns false
8658          * @type String
8659          */
8660         'alphaText' : 'This field should only contain letters and _',
8661         /**
8662          * The keystroke filter mask to be applied on alpha input
8663          * @type RegExp
8664          */
8665         'alphaMask' : /[a-z_]/i,
8666
8667         /**
8668          * The function used to validate alphanumeric values
8669          * @param {String} value The value
8670          */
8671         'alphanum' : function(v){
8672             return alphanum.test(v);
8673         },
8674         /**
8675          * The error text to display when the alphanumeric validation function returns false
8676          * @type String
8677          */
8678         'alphanumText' : 'This field should only contain letters, numbers and _',
8679         /**
8680          * The keystroke filter mask to be applied on alphanumeric input
8681          * @type RegExp
8682          */
8683         'alphanumMask' : /[a-z0-9_]/i
8684     };
8685 }();/*
8686  * - LGPL
8687  *
8688  * Input
8689  * 
8690  */
8691
8692 /**
8693  * @class Roo.bootstrap.Input
8694  * @extends Roo.bootstrap.Component
8695  * Bootstrap Input class
8696  * @cfg {Boolean} disabled is it disabled
8697  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8698  * @cfg {String} name name of the input
8699  * @cfg {string} fieldLabel - the label associated
8700  * @cfg {string} placeholder - placeholder to put in text.
8701  * @cfg {string}  before - input group add on before
8702  * @cfg {string} after - input group add on after
8703  * @cfg {string} size - (lg|sm) or leave empty..
8704  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8705  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8706  * @cfg {Number} md colspan out of 12 for computer-sized screens
8707  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8708  * @cfg {string} value default value of the input
8709  * @cfg {Number} labelWidth set the width of label 
8710  * @cfg {Number} labellg set the width of label (1-12)
8711  * @cfg {Number} labelmd set the width of label (1-12)
8712  * @cfg {Number} labelsm set the width of label (1-12)
8713  * @cfg {Number} labelxs set the width of label (1-12)
8714  * @cfg {String} labelAlign (top|left)
8715  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8716  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8717  * @cfg {String} indicatorpos (left|right) default left
8718  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8719  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8720
8721  * @cfg {String} align (left|center|right) Default left
8722  * @cfg {Boolean} forceFeedback (true|false) Default false
8723  * 
8724  * @constructor
8725  * Create a new Input
8726  * @param {Object} config The config object
8727  */
8728
8729 Roo.bootstrap.Input = function(config){
8730     
8731     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8732     
8733     this.addEvents({
8734         /**
8735          * @event focus
8736          * Fires when this field receives input focus.
8737          * @param {Roo.form.Field} this
8738          */
8739         focus : true,
8740         /**
8741          * @event blur
8742          * Fires when this field loses input focus.
8743          * @param {Roo.form.Field} this
8744          */
8745         blur : true,
8746         /**
8747          * @event specialkey
8748          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8749          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8750          * @param {Roo.form.Field} this
8751          * @param {Roo.EventObject} e The event object
8752          */
8753         specialkey : true,
8754         /**
8755          * @event change
8756          * Fires just before the field blurs if the field value has changed.
8757          * @param {Roo.form.Field} this
8758          * @param {Mixed} newValue The new value
8759          * @param {Mixed} oldValue The original value
8760          */
8761         change : true,
8762         /**
8763          * @event invalid
8764          * Fires after the field has been marked as invalid.
8765          * @param {Roo.form.Field} this
8766          * @param {String} msg The validation message
8767          */
8768         invalid : true,
8769         /**
8770          * @event valid
8771          * Fires after the field has been validated with no errors.
8772          * @param {Roo.form.Field} this
8773          */
8774         valid : true,
8775          /**
8776          * @event keyup
8777          * Fires after the key up
8778          * @param {Roo.form.Field} this
8779          * @param {Roo.EventObject}  e The event Object
8780          */
8781         keyup : true
8782     });
8783 };
8784
8785 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8786      /**
8787      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8788       automatic validation (defaults to "keyup").
8789      */
8790     validationEvent : "keyup",
8791      /**
8792      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8793      */
8794     validateOnBlur : true,
8795     /**
8796      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8797      */
8798     validationDelay : 250,
8799      /**
8800      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8801      */
8802     focusClass : "x-form-focus",  // not needed???
8803     
8804        
8805     /**
8806      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8807      */
8808     invalidClass : "has-warning",
8809     
8810     /**
8811      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8812      */
8813     validClass : "has-success",
8814     
8815     /**
8816      * @cfg {Boolean} hasFeedback (true|false) default true
8817      */
8818     hasFeedback : true,
8819     
8820     /**
8821      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8822      */
8823     invalidFeedbackClass : "glyphicon-warning-sign",
8824     
8825     /**
8826      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8827      */
8828     validFeedbackClass : "glyphicon-ok",
8829     
8830     /**
8831      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8832      */
8833     selectOnFocus : false,
8834     
8835      /**
8836      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8837      */
8838     maskRe : null,
8839        /**
8840      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8841      */
8842     vtype : null,
8843     
8844       /**
8845      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8846      */
8847     disableKeyFilter : false,
8848     
8849        /**
8850      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8851      */
8852     disabled : false,
8853      /**
8854      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8855      */
8856     allowBlank : true,
8857     /**
8858      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8859      */
8860     blankText : "Please complete this mandatory field",
8861     
8862      /**
8863      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8864      */
8865     minLength : 0,
8866     /**
8867      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8868      */
8869     maxLength : Number.MAX_VALUE,
8870     /**
8871      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8872      */
8873     minLengthText : "The minimum length for this field is {0}",
8874     /**
8875      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8876      */
8877     maxLengthText : "The maximum length for this field is {0}",
8878   
8879     
8880     /**
8881      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8882      * If available, this function will be called only after the basic validators all return true, and will be passed the
8883      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8884      */
8885     validator : null,
8886     /**
8887      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8888      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8889      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8890      */
8891     regex : null,
8892     /**
8893      * @cfg {String} regexText -- Depricated - use Invalid Text
8894      */
8895     regexText : "",
8896     
8897     /**
8898      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8899      */
8900     invalidText : "",
8901     
8902     
8903     
8904     autocomplete: false,
8905     
8906     
8907     fieldLabel : '',
8908     inputType : 'text',
8909     
8910     name : false,
8911     placeholder: false,
8912     before : false,
8913     after : false,
8914     size : false,
8915     hasFocus : false,
8916     preventMark: false,
8917     isFormField : true,
8918     value : '',
8919     labelWidth : 2,
8920     labelAlign : false,
8921     readOnly : false,
8922     align : false,
8923     formatedValue : false,
8924     forceFeedback : false,
8925     
8926     indicatorpos : 'left',
8927     
8928     labellg : 0,
8929     labelmd : 0,
8930     labelsm : 0,
8931     labelxs : 0,
8932     
8933     capture : '',
8934     accept : '',
8935     
8936     parentLabelAlign : function()
8937     {
8938         var parent = this;
8939         while (parent.parent()) {
8940             parent = parent.parent();
8941             if (typeof(parent.labelAlign) !='undefined') {
8942                 return parent.labelAlign;
8943             }
8944         }
8945         return 'left';
8946         
8947     },
8948     
8949     getAutoCreate : function()
8950     {
8951         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8952         
8953         var id = Roo.id();
8954         
8955         var cfg = {};
8956         
8957         if(this.inputType != 'hidden'){
8958             cfg.cls = 'form-group' //input-group
8959         }
8960         
8961         var input =  {
8962             tag: 'input',
8963             id : id,
8964             type : this.inputType,
8965             value : this.value,
8966             cls : 'form-control',
8967             placeholder : this.placeholder || '',
8968             autocomplete : this.autocomplete || 'new-password'
8969         };
8970         
8971         if(this.capture.length){
8972             input.capture = this.capture;
8973         }
8974         
8975         if(this.accept.length){
8976             input.accept = this.accept + "/*";
8977         }
8978         
8979         if(this.align){
8980             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8981         }
8982         
8983         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8984             input.maxLength = this.maxLength;
8985         }
8986         
8987         if (this.disabled) {
8988             input.disabled=true;
8989         }
8990         
8991         if (this.readOnly) {
8992             input.readonly=true;
8993         }
8994         
8995         if (this.name) {
8996             input.name = this.name;
8997         }
8998         
8999         if (this.size) {
9000             input.cls += ' input-' + this.size;
9001         }
9002         
9003         var settings=this;
9004         ['xs','sm','md','lg'].map(function(size){
9005             if (settings[size]) {
9006                 cfg.cls += ' col-' + size + '-' + settings[size];
9007             }
9008         });
9009         
9010         var inputblock = input;
9011         
9012         var feedback = {
9013             tag: 'span',
9014             cls: 'glyphicon form-control-feedback'
9015         };
9016             
9017         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9018             
9019             inputblock = {
9020                 cls : 'has-feedback',
9021                 cn :  [
9022                     input,
9023                     feedback
9024                 ] 
9025             };  
9026         }
9027         
9028         if (this.before || this.after) {
9029             
9030             inputblock = {
9031                 cls : 'input-group',
9032                 cn :  [] 
9033             };
9034             
9035             if (this.before && typeof(this.before) == 'string') {
9036                 
9037                 inputblock.cn.push({
9038                     tag :'span',
9039                     cls : 'roo-input-before input-group-addon',
9040                     html : this.before
9041                 });
9042             }
9043             if (this.before && typeof(this.before) == 'object') {
9044                 this.before = Roo.factory(this.before);
9045                 
9046                 inputblock.cn.push({
9047                     tag :'span',
9048                     cls : 'roo-input-before input-group-' +
9049                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9050                 });
9051             }
9052             
9053             inputblock.cn.push(input);
9054             
9055             if (this.after && typeof(this.after) == 'string') {
9056                 inputblock.cn.push({
9057                     tag :'span',
9058                     cls : 'roo-input-after input-group-addon',
9059                     html : this.after
9060                 });
9061             }
9062             if (this.after && typeof(this.after) == 'object') {
9063                 this.after = Roo.factory(this.after);
9064                 
9065                 inputblock.cn.push({
9066                     tag :'span',
9067                     cls : 'roo-input-after input-group-' +
9068                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9069                 });
9070             }
9071             
9072             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9073                 inputblock.cls += ' has-feedback';
9074                 inputblock.cn.push(feedback);
9075             }
9076         };
9077         
9078         if (align ==='left' && this.fieldLabel.length) {
9079             
9080             cfg.cls += ' roo-form-group-label-left';
9081             
9082             cfg.cn = [
9083                 {
9084                     tag : 'i',
9085                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9086                     tooltip : 'This field is required'
9087                 },
9088                 {
9089                     tag: 'label',
9090                     'for' :  id,
9091                     cls : 'control-label',
9092                     html : this.fieldLabel
9093
9094                 },
9095                 {
9096                     cls : "", 
9097                     cn: [
9098                         inputblock
9099                     ]
9100                 }
9101             ];
9102             
9103             var labelCfg = cfg.cn[1];
9104             var contentCfg = cfg.cn[2];
9105             
9106             if(this.indicatorpos == 'right'){
9107                 cfg.cn = [
9108                     {
9109                         tag: 'label',
9110                         'for' :  id,
9111                         cls : 'control-label',
9112                         cn : [
9113                             {
9114                                 tag : 'span',
9115                                 html : this.fieldLabel
9116                             },
9117                             {
9118                                 tag : 'i',
9119                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9120                                 tooltip : 'This field is required'
9121                             }
9122                         ]
9123                     },
9124                     {
9125                         cls : "",
9126                         cn: [
9127                             inputblock
9128                         ]
9129                     }
9130
9131                 ];
9132                 
9133                 labelCfg = cfg.cn[0];
9134                 contentCfg = cfg.cn[1];
9135             
9136             }
9137             
9138             if(this.labelWidth > 12){
9139                 labelCfg.style = "width: " + this.labelWidth + 'px';
9140             }
9141             
9142             if(this.labelWidth < 13 && this.labelmd == 0){
9143                 this.labelmd = this.labelWidth;
9144             }
9145             
9146             if(this.labellg > 0){
9147                 labelCfg.cls += ' col-lg-' + this.labellg;
9148                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9149             }
9150             
9151             if(this.labelmd > 0){
9152                 labelCfg.cls += ' col-md-' + this.labelmd;
9153                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9154             }
9155             
9156             if(this.labelsm > 0){
9157                 labelCfg.cls += ' col-sm-' + this.labelsm;
9158                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9159             }
9160             
9161             if(this.labelxs > 0){
9162                 labelCfg.cls += ' col-xs-' + this.labelxs;
9163                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9164             }
9165             
9166             
9167         } else if ( this.fieldLabel.length) {
9168                 
9169             cfg.cn = [
9170                 {
9171                     tag : 'i',
9172                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9173                     tooltip : 'This field is required'
9174                 },
9175                 {
9176                     tag: 'label',
9177                    //cls : 'input-group-addon',
9178                     html : this.fieldLabel
9179
9180                 },
9181
9182                inputblock
9183
9184            ];
9185            
9186            if(this.indicatorpos == 'right'){
9187                 
9188                 cfg.cn = [
9189                     {
9190                         tag: 'label',
9191                        //cls : 'input-group-addon',
9192                         html : this.fieldLabel
9193
9194                     },
9195                     {
9196                         tag : 'i',
9197                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9198                         tooltip : 'This field is required'
9199                     },
9200
9201                    inputblock
9202
9203                ];
9204
9205             }
9206
9207         } else {
9208             
9209             cfg.cn = [
9210
9211                     inputblock
9212
9213             ];
9214                 
9215                 
9216         };
9217         
9218         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9219            cfg.cls += ' navbar-form';
9220         }
9221         
9222         if (this.parentType === 'NavGroup') {
9223            cfg.cls += ' navbar-form';
9224            cfg.tag = 'li';
9225         }
9226         
9227         return cfg;
9228         
9229     },
9230     /**
9231      * return the real input element.
9232      */
9233     inputEl: function ()
9234     {
9235         return this.el.select('input.form-control',true).first();
9236     },
9237     
9238     tooltipEl : function()
9239     {
9240         return this.inputEl();
9241     },
9242     
9243     indicatorEl : function()
9244     {
9245         var indicator = this.el.select('i.roo-required-indicator',true).first();
9246         
9247         if(!indicator){
9248             return false;
9249         }
9250         
9251         return indicator;
9252         
9253     },
9254     
9255     setDisabled : function(v)
9256     {
9257         var i  = this.inputEl().dom;
9258         if (!v) {
9259             i.removeAttribute('disabled');
9260             return;
9261             
9262         }
9263         i.setAttribute('disabled','true');
9264     },
9265     initEvents : function()
9266     {
9267           
9268         this.inputEl().on("keydown" , this.fireKey,  this);
9269         this.inputEl().on("focus", this.onFocus,  this);
9270         this.inputEl().on("blur", this.onBlur,  this);
9271         
9272         this.inputEl().relayEvent('keyup', this);
9273         
9274         this.indicator = this.indicatorEl();
9275         
9276         if(this.indicator){
9277             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9278         }
9279  
9280         // reference to original value for reset
9281         this.originalValue = this.getValue();
9282         //Roo.form.TextField.superclass.initEvents.call(this);
9283         if(this.validationEvent == 'keyup'){
9284             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9285             this.inputEl().on('keyup', this.filterValidation, this);
9286         }
9287         else if(this.validationEvent !== false){
9288             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9289         }
9290         
9291         if(this.selectOnFocus){
9292             this.on("focus", this.preFocus, this);
9293             
9294         }
9295         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9296             this.inputEl().on("keypress", this.filterKeys, this);
9297         } else {
9298             this.inputEl().relayEvent('keypress', this);
9299         }
9300        /* if(this.grow){
9301             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9302             this.el.on("click", this.autoSize,  this);
9303         }
9304         */
9305         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9306             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9307         }
9308         
9309         if (typeof(this.before) == 'object') {
9310             this.before.render(this.el.select('.roo-input-before',true).first());
9311         }
9312         if (typeof(this.after) == 'object') {
9313             this.after.render(this.el.select('.roo-input-after',true).first());
9314         }
9315         
9316         this.inputEl().on('change', this.onChange, this);
9317         
9318     },
9319     filterValidation : function(e){
9320         if(!e.isNavKeyPress()){
9321             this.validationTask.delay(this.validationDelay);
9322         }
9323     },
9324      /**
9325      * Validates the field value
9326      * @return {Boolean} True if the value is valid, else false
9327      */
9328     validate : function(){
9329         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9330         if(this.disabled || this.validateValue(this.getRawValue())){
9331             this.markValid();
9332             return true;
9333         }
9334         
9335         this.markInvalid();
9336         return false;
9337     },
9338     
9339     
9340     /**
9341      * Validates a value according to the field's validation rules and marks the field as invalid
9342      * if the validation fails
9343      * @param {Mixed} value The value to validate
9344      * @return {Boolean} True if the value is valid, else false
9345      */
9346     validateValue : function(value)
9347     {
9348         if(this.getVisibilityEl().hasClass('hidden')){
9349             return true;
9350         }
9351         
9352         if(value.length < 1)  { // if it's blank
9353             if(this.allowBlank){
9354                 return true;
9355             }
9356             return false;
9357         }
9358         
9359         if(value.length < this.minLength){
9360             return false;
9361         }
9362         if(value.length > this.maxLength){
9363             return false;
9364         }
9365         if(this.vtype){
9366             var vt = Roo.form.VTypes;
9367             if(!vt[this.vtype](value, this)){
9368                 return false;
9369             }
9370         }
9371         if(typeof this.validator == "function"){
9372             var msg = this.validator(value);
9373             if(msg !== true){
9374                 return false;
9375             }
9376             if (typeof(msg) == 'string') {
9377                 this.invalidText = msg;
9378             }
9379         }
9380         
9381         if(this.regex && !this.regex.test(value)){
9382             return false;
9383         }
9384         
9385         return true;
9386     },
9387     
9388      // private
9389     fireKey : function(e){
9390         //Roo.log('field ' + e.getKey());
9391         if(e.isNavKeyPress()){
9392             this.fireEvent("specialkey", this, e);
9393         }
9394     },
9395     focus : function (selectText){
9396         if(this.rendered){
9397             this.inputEl().focus();
9398             if(selectText === true){
9399                 this.inputEl().dom.select();
9400             }
9401         }
9402         return this;
9403     } ,
9404     
9405     onFocus : function(){
9406         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9407            // this.el.addClass(this.focusClass);
9408         }
9409         if(!this.hasFocus){
9410             this.hasFocus = true;
9411             this.startValue = this.getValue();
9412             this.fireEvent("focus", this);
9413         }
9414     },
9415     
9416     beforeBlur : Roo.emptyFn,
9417
9418     
9419     // private
9420     onBlur : function(){
9421         this.beforeBlur();
9422         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9423             //this.el.removeClass(this.focusClass);
9424         }
9425         this.hasFocus = false;
9426         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9427             this.validate();
9428         }
9429         var v = this.getValue();
9430         if(String(v) !== String(this.startValue)){
9431             this.fireEvent('change', this, v, this.startValue);
9432         }
9433         this.fireEvent("blur", this);
9434     },
9435     
9436     onChange : function(e)
9437     {
9438         var v = this.getValue();
9439         if(String(v) !== String(this.startValue)){
9440             this.fireEvent('change', this, v, this.startValue);
9441         }
9442         
9443     },
9444     
9445     /**
9446      * Resets the current field value to the originally loaded value and clears any validation messages
9447      */
9448     reset : function(){
9449         this.setValue(this.originalValue);
9450         this.validate();
9451     },
9452      /**
9453      * Returns the name of the field
9454      * @return {Mixed} name The name field
9455      */
9456     getName: function(){
9457         return this.name;
9458     },
9459      /**
9460      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9461      * @return {Mixed} value The field value
9462      */
9463     getValue : function(){
9464         
9465         var v = this.inputEl().getValue();
9466         
9467         return v;
9468     },
9469     /**
9470      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9471      * @return {Mixed} value The field value
9472      */
9473     getRawValue : function(){
9474         var v = this.inputEl().getValue();
9475         
9476         return v;
9477     },
9478     
9479     /**
9480      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9481      * @param {Mixed} value The value to set
9482      */
9483     setRawValue : function(v){
9484         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9485     },
9486     
9487     selectText : function(start, end){
9488         var v = this.getRawValue();
9489         if(v.length > 0){
9490             start = start === undefined ? 0 : start;
9491             end = end === undefined ? v.length : end;
9492             var d = this.inputEl().dom;
9493             if(d.setSelectionRange){
9494                 d.setSelectionRange(start, end);
9495             }else if(d.createTextRange){
9496                 var range = d.createTextRange();
9497                 range.moveStart("character", start);
9498                 range.moveEnd("character", v.length-end);
9499                 range.select();
9500             }
9501         }
9502     },
9503     
9504     /**
9505      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9506      * @param {Mixed} value The value to set
9507      */
9508     setValue : function(v){
9509         this.value = v;
9510         if(this.rendered){
9511             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9512             this.validate();
9513         }
9514     },
9515     
9516     /*
9517     processValue : function(value){
9518         if(this.stripCharsRe){
9519             var newValue = value.replace(this.stripCharsRe, '');
9520             if(newValue !== value){
9521                 this.setRawValue(newValue);
9522                 return newValue;
9523             }
9524         }
9525         return value;
9526     },
9527   */
9528     preFocus : function(){
9529         
9530         if(this.selectOnFocus){
9531             this.inputEl().dom.select();
9532         }
9533     },
9534     filterKeys : function(e){
9535         var k = e.getKey();
9536         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9537             return;
9538         }
9539         var c = e.getCharCode(), cc = String.fromCharCode(c);
9540         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9541             return;
9542         }
9543         if(!this.maskRe.test(cc)){
9544             e.stopEvent();
9545         }
9546     },
9547      /**
9548      * Clear any invalid styles/messages for this field
9549      */
9550     clearInvalid : function(){
9551         
9552         if(!this.el || this.preventMark){ // not rendered
9553             return;
9554         }
9555         
9556      
9557         this.el.removeClass(this.invalidClass);
9558         
9559         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9560             
9561             var feedback = this.el.select('.form-control-feedback', true).first();
9562             
9563             if(feedback){
9564                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9565             }
9566             
9567         }
9568         
9569         if(this.indicator){
9570             this.indicator.removeClass('visible');
9571             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9572         }
9573         
9574         this.fireEvent('valid', this);
9575     },
9576     
9577      /**
9578      * Mark this field as valid
9579      */
9580     markValid : function()
9581     {
9582         if(!this.el  || this.preventMark){ // not rendered...
9583             return;
9584         }
9585         
9586         this.el.removeClass([this.invalidClass, this.validClass]);
9587         
9588         var feedback = this.el.select('.form-control-feedback', true).first();
9589             
9590         if(feedback){
9591             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9592         }
9593         
9594         if(this.indicator){
9595             this.indicator.removeClass('visible');
9596             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9597         }
9598         
9599         if(this.disabled){
9600             return;
9601         }
9602         
9603         if(this.allowBlank && !this.getRawValue().length){
9604             return;
9605         }
9606         
9607         this.el.addClass(this.validClass);
9608         
9609         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9610             
9611             var feedback = this.el.select('.form-control-feedback', true).first();
9612             
9613             if(feedback){
9614                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9615                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9616             }
9617             
9618         }
9619         
9620         this.fireEvent('valid', this);
9621     },
9622     
9623      /**
9624      * Mark this field as invalid
9625      * @param {String} msg The validation message
9626      */
9627     markInvalid : function(msg)
9628     {
9629         if(!this.el  || this.preventMark){ // not rendered
9630             return;
9631         }
9632         
9633         this.el.removeClass([this.invalidClass, this.validClass]);
9634         
9635         var feedback = this.el.select('.form-control-feedback', true).first();
9636             
9637         if(feedback){
9638             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9639         }
9640
9641         if(this.disabled){
9642             return;
9643         }
9644         
9645         if(this.allowBlank && !this.getRawValue().length){
9646             return;
9647         }
9648         
9649         if(this.indicator){
9650             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9651             this.indicator.addClass('visible');
9652         }
9653         
9654         this.el.addClass(this.invalidClass);
9655         
9656         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9657             
9658             var feedback = this.el.select('.form-control-feedback', true).first();
9659             
9660             if(feedback){
9661                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9662                 
9663                 if(this.getValue().length || this.forceFeedback){
9664                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9665                 }
9666                 
9667             }
9668             
9669         }
9670         
9671         this.fireEvent('invalid', this, msg);
9672     },
9673     // private
9674     SafariOnKeyDown : function(event)
9675     {
9676         // this is a workaround for a password hang bug on chrome/ webkit.
9677         if (this.inputEl().dom.type != 'password') {
9678             return;
9679         }
9680         
9681         var isSelectAll = false;
9682         
9683         if(this.inputEl().dom.selectionEnd > 0){
9684             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9685         }
9686         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9687             event.preventDefault();
9688             this.setValue('');
9689             return;
9690         }
9691         
9692         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9693             
9694             event.preventDefault();
9695             // this is very hacky as keydown always get's upper case.
9696             //
9697             var cc = String.fromCharCode(event.getCharCode());
9698             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9699             
9700         }
9701     },
9702     adjustWidth : function(tag, w){
9703         tag = tag.toLowerCase();
9704         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9705             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9706                 if(tag == 'input'){
9707                     return w + 2;
9708                 }
9709                 if(tag == 'textarea'){
9710                     return w-2;
9711                 }
9712             }else if(Roo.isOpera){
9713                 if(tag == 'input'){
9714                     return w + 2;
9715                 }
9716                 if(tag == 'textarea'){
9717                     return w-2;
9718                 }
9719             }
9720         }
9721         return w;
9722     },
9723     
9724     setFieldLabel : function(v)
9725     {
9726         if(!this.rendered){
9727             return;
9728         }
9729         
9730         if(this.indicator){
9731             var ar = this.el.select('label > span',true);
9732             
9733             if (ar.elements.length) {
9734                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9735                 this.fieldLabel = v;
9736                 return;
9737             }
9738             
9739             var br = this.el.select('label',true);
9740             
9741             if(br.elements.length) {
9742                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9743                 this.fieldLabel = v;
9744                 return;
9745             }
9746             
9747             Roo.log('Cannot Found any of label > span || label in input');
9748             return;
9749         }
9750         
9751         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9752         this.fieldLabel = v;
9753         
9754         
9755     }
9756 });
9757
9758  
9759 /*
9760  * - LGPL
9761  *
9762  * Input
9763  * 
9764  */
9765
9766 /**
9767  * @class Roo.bootstrap.TextArea
9768  * @extends Roo.bootstrap.Input
9769  * Bootstrap TextArea class
9770  * @cfg {Number} cols Specifies the visible width of a text area
9771  * @cfg {Number} rows Specifies the visible number of lines in a text area
9772  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9773  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9774  * @cfg {string} html text
9775  * 
9776  * @constructor
9777  * Create a new TextArea
9778  * @param {Object} config The config object
9779  */
9780
9781 Roo.bootstrap.TextArea = function(config){
9782     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9783    
9784 };
9785
9786 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9787      
9788     cols : false,
9789     rows : 5,
9790     readOnly : false,
9791     warp : 'soft',
9792     resize : false,
9793     value: false,
9794     html: false,
9795     
9796     getAutoCreate : function(){
9797         
9798         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9799         
9800         var id = Roo.id();
9801         
9802         var cfg = {};
9803         
9804         if(this.inputType != 'hidden'){
9805             cfg.cls = 'form-group' //input-group
9806         }
9807         
9808         var input =  {
9809             tag: 'textarea',
9810             id : id,
9811             warp : this.warp,
9812             rows : this.rows,
9813             value : this.value || '',
9814             html: this.html || '',
9815             cls : 'form-control',
9816             placeholder : this.placeholder || '' 
9817             
9818         };
9819         
9820         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9821             input.maxLength = this.maxLength;
9822         }
9823         
9824         if(this.resize){
9825             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9826         }
9827         
9828         if(this.cols){
9829             input.cols = this.cols;
9830         }
9831         
9832         if (this.readOnly) {
9833             input.readonly = true;
9834         }
9835         
9836         if (this.name) {
9837             input.name = this.name;
9838         }
9839         
9840         if (this.size) {
9841             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9842         }
9843         
9844         var settings=this;
9845         ['xs','sm','md','lg'].map(function(size){
9846             if (settings[size]) {
9847                 cfg.cls += ' col-' + size + '-' + settings[size];
9848             }
9849         });
9850         
9851         var inputblock = input;
9852         
9853         if(this.hasFeedback && !this.allowBlank){
9854             
9855             var feedback = {
9856                 tag: 'span',
9857                 cls: 'glyphicon form-control-feedback'
9858             };
9859
9860             inputblock = {
9861                 cls : 'has-feedback',
9862                 cn :  [
9863                     input,
9864                     feedback
9865                 ] 
9866             };  
9867         }
9868         
9869         
9870         if (this.before || this.after) {
9871             
9872             inputblock = {
9873                 cls : 'input-group',
9874                 cn :  [] 
9875             };
9876             if (this.before) {
9877                 inputblock.cn.push({
9878                     tag :'span',
9879                     cls : 'input-group-addon',
9880                     html : this.before
9881                 });
9882             }
9883             
9884             inputblock.cn.push(input);
9885             
9886             if(this.hasFeedback && !this.allowBlank){
9887                 inputblock.cls += ' has-feedback';
9888                 inputblock.cn.push(feedback);
9889             }
9890             
9891             if (this.after) {
9892                 inputblock.cn.push({
9893                     tag :'span',
9894                     cls : 'input-group-addon',
9895                     html : this.after
9896                 });
9897             }
9898             
9899         }
9900         
9901         if (align ==='left' && this.fieldLabel.length) {
9902             cfg.cn = [
9903                 {
9904                     tag: 'label',
9905                     'for' :  id,
9906                     cls : 'control-label',
9907                     html : this.fieldLabel
9908                 },
9909                 {
9910                     cls : "",
9911                     cn: [
9912                         inputblock
9913                     ]
9914                 }
9915
9916             ];
9917             
9918             if(this.labelWidth > 12){
9919                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9920             }
9921
9922             if(this.labelWidth < 13 && this.labelmd == 0){
9923                 this.labelmd = this.labelWidth;
9924             }
9925
9926             if(this.labellg > 0){
9927                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9928                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9929             }
9930
9931             if(this.labelmd > 0){
9932                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9933                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9934             }
9935
9936             if(this.labelsm > 0){
9937                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9938                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9939             }
9940
9941             if(this.labelxs > 0){
9942                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9943                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9944             }
9945             
9946         } else if ( this.fieldLabel.length) {
9947             cfg.cn = [
9948
9949                {
9950                    tag: 'label',
9951                    //cls : 'input-group-addon',
9952                    html : this.fieldLabel
9953
9954                },
9955
9956                inputblock
9957
9958            ];
9959
9960         } else {
9961
9962             cfg.cn = [
9963
9964                 inputblock
9965
9966             ];
9967                 
9968         }
9969         
9970         if (this.disabled) {
9971             input.disabled=true;
9972         }
9973         
9974         return cfg;
9975         
9976     },
9977     /**
9978      * return the real textarea element.
9979      */
9980     inputEl: function ()
9981     {
9982         return this.el.select('textarea.form-control',true).first();
9983     },
9984     
9985     /**
9986      * Clear any invalid styles/messages for this field
9987      */
9988     clearInvalid : function()
9989     {
9990         
9991         if(!this.el || this.preventMark){ // not rendered
9992             return;
9993         }
9994         
9995         var label = this.el.select('label', true).first();
9996         var icon = this.el.select('i.fa-star', true).first();
9997         
9998         if(label && icon){
9999             icon.remove();
10000         }
10001         
10002         this.el.removeClass(this.invalidClass);
10003         
10004         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10005             
10006             var feedback = this.el.select('.form-control-feedback', true).first();
10007             
10008             if(feedback){
10009                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10010             }
10011             
10012         }
10013         
10014         this.fireEvent('valid', this);
10015     },
10016     
10017      /**
10018      * Mark this field as valid
10019      */
10020     markValid : function()
10021     {
10022         if(!this.el  || this.preventMark){ // not rendered
10023             return;
10024         }
10025         
10026         this.el.removeClass([this.invalidClass, this.validClass]);
10027         
10028         var feedback = this.el.select('.form-control-feedback', true).first();
10029             
10030         if(feedback){
10031             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10032         }
10033
10034         if(this.disabled || this.allowBlank){
10035             return;
10036         }
10037         
10038         var label = this.el.select('label', true).first();
10039         var icon = this.el.select('i.fa-star', true).first();
10040         
10041         if(label && icon){
10042             icon.remove();
10043         }
10044         
10045         this.el.addClass(this.validClass);
10046         
10047         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10048             
10049             var feedback = this.el.select('.form-control-feedback', true).first();
10050             
10051             if(feedback){
10052                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10053                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10054             }
10055             
10056         }
10057         
10058         this.fireEvent('valid', this);
10059     },
10060     
10061      /**
10062      * Mark this field as invalid
10063      * @param {String} msg The validation message
10064      */
10065     markInvalid : function(msg)
10066     {
10067         if(!this.el  || this.preventMark){ // not rendered
10068             return;
10069         }
10070         
10071         this.el.removeClass([this.invalidClass, this.validClass]);
10072         
10073         var feedback = this.el.select('.form-control-feedback', true).first();
10074             
10075         if(feedback){
10076             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10077         }
10078
10079         if(this.disabled || this.allowBlank){
10080             return;
10081         }
10082         
10083         var label = this.el.select('label', true).first();
10084         var icon = this.el.select('i.fa-star', true).first();
10085         
10086         if(!this.getValue().length && label && !icon){
10087             this.el.createChild({
10088                 tag : 'i',
10089                 cls : 'text-danger fa fa-lg fa-star',
10090                 tooltip : 'This field is required',
10091                 style : 'margin-right:5px;'
10092             }, label, true);
10093         }
10094
10095         this.el.addClass(this.invalidClass);
10096         
10097         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10098             
10099             var feedback = this.el.select('.form-control-feedback', true).first();
10100             
10101             if(feedback){
10102                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10103                 
10104                 if(this.getValue().length || this.forceFeedback){
10105                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10106                 }
10107                 
10108             }
10109             
10110         }
10111         
10112         this.fireEvent('invalid', this, msg);
10113     }
10114 });
10115
10116  
10117 /*
10118  * - LGPL
10119  *
10120  * trigger field - base class for combo..
10121  * 
10122  */
10123  
10124 /**
10125  * @class Roo.bootstrap.TriggerField
10126  * @extends Roo.bootstrap.Input
10127  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10128  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10129  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10130  * for which you can provide a custom implementation.  For example:
10131  * <pre><code>
10132 var trigger = new Roo.bootstrap.TriggerField();
10133 trigger.onTriggerClick = myTriggerFn;
10134 trigger.applyTo('my-field');
10135 </code></pre>
10136  *
10137  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10138  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10139  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10140  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10141  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10142
10143  * @constructor
10144  * Create a new TriggerField.
10145  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10146  * to the base TextField)
10147  */
10148 Roo.bootstrap.TriggerField = function(config){
10149     this.mimicing = false;
10150     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10151 };
10152
10153 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10154     /**
10155      * @cfg {String} triggerClass A CSS class to apply to the trigger
10156      */
10157      /**
10158      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10159      */
10160     hideTrigger:false,
10161
10162     /**
10163      * @cfg {Boolean} removable (true|false) special filter default false
10164      */
10165     removable : false,
10166     
10167     /** @cfg {Boolean} grow @hide */
10168     /** @cfg {Number} growMin @hide */
10169     /** @cfg {Number} growMax @hide */
10170
10171     /**
10172      * @hide 
10173      * @method
10174      */
10175     autoSize: Roo.emptyFn,
10176     // private
10177     monitorTab : true,
10178     // private
10179     deferHeight : true,
10180
10181     
10182     actionMode : 'wrap',
10183     
10184     caret : false,
10185     
10186     
10187     getAutoCreate : function(){
10188        
10189         var align = this.labelAlign || this.parentLabelAlign();
10190         
10191         var id = Roo.id();
10192         
10193         var cfg = {
10194             cls: 'form-group' //input-group
10195         };
10196         
10197         
10198         var input =  {
10199             tag: 'input',
10200             id : id,
10201             type : this.inputType,
10202             cls : 'form-control',
10203             autocomplete: 'new-password',
10204             placeholder : this.placeholder || '' 
10205             
10206         };
10207         if (this.name) {
10208             input.name = this.name;
10209         }
10210         if (this.size) {
10211             input.cls += ' input-' + this.size;
10212         }
10213         
10214         if (this.disabled) {
10215             input.disabled=true;
10216         }
10217         
10218         var inputblock = input;
10219         
10220         if(this.hasFeedback && !this.allowBlank){
10221             
10222             var feedback = {
10223                 tag: 'span',
10224                 cls: 'glyphicon form-control-feedback'
10225             };
10226             
10227             if(this.removable && !this.editable && !this.tickable){
10228                 inputblock = {
10229                     cls : 'has-feedback',
10230                     cn :  [
10231                         inputblock,
10232                         {
10233                             tag: 'button',
10234                             html : 'x',
10235                             cls : 'roo-combo-removable-btn close'
10236                         },
10237                         feedback
10238                     ] 
10239                 };
10240             } else {
10241                 inputblock = {
10242                     cls : 'has-feedback',
10243                     cn :  [
10244                         inputblock,
10245                         feedback
10246                     ] 
10247                 };
10248             }
10249
10250         } else {
10251             if(this.removable && !this.editable && !this.tickable){
10252                 inputblock = {
10253                     cls : 'roo-removable',
10254                     cn :  [
10255                         inputblock,
10256                         {
10257                             tag: 'button',
10258                             html : 'x',
10259                             cls : 'roo-combo-removable-btn close'
10260                         }
10261                     ] 
10262                 };
10263             }
10264         }
10265         
10266         if (this.before || this.after) {
10267             
10268             inputblock = {
10269                 cls : 'input-group',
10270                 cn :  [] 
10271             };
10272             if (this.before) {
10273                 inputblock.cn.push({
10274                     tag :'span',
10275                     cls : 'input-group-addon',
10276                     html : this.before
10277                 });
10278             }
10279             
10280             inputblock.cn.push(input);
10281             
10282             if(this.hasFeedback && !this.allowBlank){
10283                 inputblock.cls += ' has-feedback';
10284                 inputblock.cn.push(feedback);
10285             }
10286             
10287             if (this.after) {
10288                 inputblock.cn.push({
10289                     tag :'span',
10290                     cls : 'input-group-addon',
10291                     html : this.after
10292                 });
10293             }
10294             
10295         };
10296         
10297         var box = {
10298             tag: 'div',
10299             cn: [
10300                 {
10301                     tag: 'input',
10302                     type : 'hidden',
10303                     cls: 'form-hidden-field'
10304                 },
10305                 inputblock
10306             ]
10307             
10308         };
10309         
10310         if(this.multiple){
10311             box = {
10312                 tag: 'div',
10313                 cn: [
10314                     {
10315                         tag: 'input',
10316                         type : 'hidden',
10317                         cls: 'form-hidden-field'
10318                     },
10319                     {
10320                         tag: 'ul',
10321                         cls: 'roo-select2-choices',
10322                         cn:[
10323                             {
10324                                 tag: 'li',
10325                                 cls: 'roo-select2-search-field',
10326                                 cn: [
10327
10328                                     inputblock
10329                                 ]
10330                             }
10331                         ]
10332                     }
10333                 ]
10334             }
10335         };
10336         
10337         var combobox = {
10338             cls: 'roo-select2-container input-group',
10339             cn: [
10340                 box
10341 //                {
10342 //                    tag: 'ul',
10343 //                    cls: 'typeahead typeahead-long dropdown-menu',
10344 //                    style: 'display:none'
10345 //                }
10346             ]
10347         };
10348         
10349         if(!this.multiple && this.showToggleBtn){
10350             
10351             var caret = {
10352                         tag: 'span',
10353                         cls: 'caret'
10354              };
10355             if (this.caret != false) {
10356                 caret = {
10357                      tag: 'i',
10358                      cls: 'fa fa-' + this.caret
10359                 };
10360                 
10361             }
10362             
10363             combobox.cn.push({
10364                 tag :'span',
10365                 cls : 'input-group-addon btn dropdown-toggle',
10366                 cn : [
10367                     caret,
10368                     {
10369                         tag: 'span',
10370                         cls: 'combobox-clear',
10371                         cn  : [
10372                             {
10373                                 tag : 'i',
10374                                 cls: 'icon-remove'
10375                             }
10376                         ]
10377                     }
10378                 ]
10379
10380             })
10381         }
10382         
10383         if(this.multiple){
10384             combobox.cls += ' roo-select2-container-multi';
10385         }
10386         
10387         if (align ==='left' && this.fieldLabel.length) {
10388             
10389             cfg.cls += ' roo-form-group-label-left';
10390
10391             cfg.cn = [
10392                 {
10393                     tag : 'i',
10394                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10395                     tooltip : 'This field is required'
10396                 },
10397                 {
10398                     tag: 'label',
10399                     'for' :  id,
10400                     cls : 'control-label',
10401                     html : this.fieldLabel
10402
10403                 },
10404                 {
10405                     cls : "", 
10406                     cn: [
10407                         combobox
10408                     ]
10409                 }
10410
10411             ];
10412             
10413             var labelCfg = cfg.cn[1];
10414             var contentCfg = cfg.cn[2];
10415             
10416             if(this.indicatorpos == 'right'){
10417                 cfg.cn = [
10418                     {
10419                         tag: 'label',
10420                         'for' :  id,
10421                         cls : 'control-label',
10422                         cn : [
10423                             {
10424                                 tag : 'span',
10425                                 html : this.fieldLabel
10426                             },
10427                             {
10428                                 tag : 'i',
10429                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10430                                 tooltip : 'This field is required'
10431                             }
10432                         ]
10433                     },
10434                     {
10435                         cls : "", 
10436                         cn: [
10437                             combobox
10438                         ]
10439                     }
10440
10441                 ];
10442                 
10443                 labelCfg = cfg.cn[0];
10444                 contentCfg = cfg.cn[1];
10445             }
10446             
10447             if(this.labelWidth > 12){
10448                 labelCfg.style = "width: " + this.labelWidth + 'px';
10449             }
10450             
10451             if(this.labelWidth < 13 && this.labelmd == 0){
10452                 this.labelmd = this.labelWidth;
10453             }
10454             
10455             if(this.labellg > 0){
10456                 labelCfg.cls += ' col-lg-' + this.labellg;
10457                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10458             }
10459             
10460             if(this.labelmd > 0){
10461                 labelCfg.cls += ' col-md-' + this.labelmd;
10462                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10463             }
10464             
10465             if(this.labelsm > 0){
10466                 labelCfg.cls += ' col-sm-' + this.labelsm;
10467                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10468             }
10469             
10470             if(this.labelxs > 0){
10471                 labelCfg.cls += ' col-xs-' + this.labelxs;
10472                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10473             }
10474             
10475         } else if ( this.fieldLabel.length) {
10476 //                Roo.log(" label");
10477             cfg.cn = [
10478                 {
10479                    tag : 'i',
10480                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10481                    tooltip : 'This field is required'
10482                },
10483                {
10484                    tag: 'label',
10485                    //cls : 'input-group-addon',
10486                    html : this.fieldLabel
10487
10488                },
10489
10490                combobox
10491
10492             ];
10493             
10494             if(this.indicatorpos == 'right'){
10495                 
10496                 cfg.cn = [
10497                     {
10498                        tag: 'label',
10499                        cn : [
10500                            {
10501                                tag : 'span',
10502                                html : this.fieldLabel
10503                            },
10504                            {
10505                               tag : 'i',
10506                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10507                               tooltip : 'This field is required'
10508                            }
10509                        ]
10510
10511                     },
10512                     combobox
10513
10514                 ];
10515
10516             }
10517
10518         } else {
10519             
10520 //                Roo.log(" no label && no align");
10521                 cfg = combobox
10522                      
10523                 
10524         }
10525         
10526         var settings=this;
10527         ['xs','sm','md','lg'].map(function(size){
10528             if (settings[size]) {
10529                 cfg.cls += ' col-' + size + '-' + settings[size];
10530             }
10531         });
10532         
10533         return cfg;
10534         
10535     },
10536     
10537     
10538     
10539     // private
10540     onResize : function(w, h){
10541 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10542 //        if(typeof w == 'number'){
10543 //            var x = w - this.trigger.getWidth();
10544 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10545 //            this.trigger.setStyle('left', x+'px');
10546 //        }
10547     },
10548
10549     // private
10550     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10551
10552     // private
10553     getResizeEl : function(){
10554         return this.inputEl();
10555     },
10556
10557     // private
10558     getPositionEl : function(){
10559         return this.inputEl();
10560     },
10561
10562     // private
10563     alignErrorIcon : function(){
10564         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10565     },
10566
10567     // private
10568     initEvents : function(){
10569         
10570         this.createList();
10571         
10572         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10573         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10574         if(!this.multiple && this.showToggleBtn){
10575             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10576             if(this.hideTrigger){
10577                 this.trigger.setDisplayed(false);
10578             }
10579             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10580         }
10581         
10582         if(this.multiple){
10583             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10584         }
10585         
10586         if(this.removable && !this.editable && !this.tickable){
10587             var close = this.closeTriggerEl();
10588             
10589             if(close){
10590                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10591                 close.on('click', this.removeBtnClick, this, close);
10592             }
10593         }
10594         
10595         //this.trigger.addClassOnOver('x-form-trigger-over');
10596         //this.trigger.addClassOnClick('x-form-trigger-click');
10597         
10598         //if(!this.width){
10599         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10600         //}
10601     },
10602     
10603     closeTriggerEl : function()
10604     {
10605         var close = this.el.select('.roo-combo-removable-btn', true).first();
10606         return close ? close : false;
10607     },
10608     
10609     removeBtnClick : function(e, h, el)
10610     {
10611         e.preventDefault();
10612         
10613         if(this.fireEvent("remove", this) !== false){
10614             this.reset();
10615             this.fireEvent("afterremove", this)
10616         }
10617     },
10618     
10619     createList : function()
10620     {
10621         this.list = Roo.get(document.body).createChild({
10622             tag: 'ul',
10623             cls: 'typeahead typeahead-long dropdown-menu',
10624             style: 'display:none'
10625         });
10626         
10627         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10628         
10629     },
10630
10631     // private
10632     initTrigger : function(){
10633        
10634     },
10635
10636     // private
10637     onDestroy : function(){
10638         if(this.trigger){
10639             this.trigger.removeAllListeners();
10640           //  this.trigger.remove();
10641         }
10642         //if(this.wrap){
10643         //    this.wrap.remove();
10644         //}
10645         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10646     },
10647
10648     // private
10649     onFocus : function(){
10650         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10651         /*
10652         if(!this.mimicing){
10653             this.wrap.addClass('x-trigger-wrap-focus');
10654             this.mimicing = true;
10655             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10656             if(this.monitorTab){
10657                 this.el.on("keydown", this.checkTab, this);
10658             }
10659         }
10660         */
10661     },
10662
10663     // private
10664     checkTab : function(e){
10665         if(e.getKey() == e.TAB){
10666             this.triggerBlur();
10667         }
10668     },
10669
10670     // private
10671     onBlur : function(){
10672         // do nothing
10673     },
10674
10675     // private
10676     mimicBlur : function(e, t){
10677         /*
10678         if(!this.wrap.contains(t) && this.validateBlur()){
10679             this.triggerBlur();
10680         }
10681         */
10682     },
10683
10684     // private
10685     triggerBlur : function(){
10686         this.mimicing = false;
10687         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10688         if(this.monitorTab){
10689             this.el.un("keydown", this.checkTab, this);
10690         }
10691         //this.wrap.removeClass('x-trigger-wrap-focus');
10692         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10693     },
10694
10695     // private
10696     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10697     validateBlur : function(e, t){
10698         return true;
10699     },
10700
10701     // private
10702     onDisable : function(){
10703         this.inputEl().dom.disabled = true;
10704         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10705         //if(this.wrap){
10706         //    this.wrap.addClass('x-item-disabled');
10707         //}
10708     },
10709
10710     // private
10711     onEnable : function(){
10712         this.inputEl().dom.disabled = false;
10713         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10714         //if(this.wrap){
10715         //    this.el.removeClass('x-item-disabled');
10716         //}
10717     },
10718
10719     // private
10720     onShow : function(){
10721         var ae = this.getActionEl();
10722         
10723         if(ae){
10724             ae.dom.style.display = '';
10725             ae.dom.style.visibility = 'visible';
10726         }
10727     },
10728
10729     // private
10730     
10731     onHide : function(){
10732         var ae = this.getActionEl();
10733         ae.dom.style.display = 'none';
10734     },
10735
10736     /**
10737      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10738      * by an implementing function.
10739      * @method
10740      * @param {EventObject} e
10741      */
10742     onTriggerClick : Roo.emptyFn
10743 });
10744  /*
10745  * Based on:
10746  * Ext JS Library 1.1.1
10747  * Copyright(c) 2006-2007, Ext JS, LLC.
10748  *
10749  * Originally Released Under LGPL - original licence link has changed is not relivant.
10750  *
10751  * Fork - LGPL
10752  * <script type="text/javascript">
10753  */
10754
10755
10756 /**
10757  * @class Roo.data.SortTypes
10758  * @singleton
10759  * Defines the default sorting (casting?) comparison functions used when sorting data.
10760  */
10761 Roo.data.SortTypes = {
10762     /**
10763      * Default sort that does nothing
10764      * @param {Mixed} s The value being converted
10765      * @return {Mixed} The comparison value
10766      */
10767     none : function(s){
10768         return s;
10769     },
10770     
10771     /**
10772      * The regular expression used to strip tags
10773      * @type {RegExp}
10774      * @property
10775      */
10776     stripTagsRE : /<\/?[^>]+>/gi,
10777     
10778     /**
10779      * Strips all HTML tags to sort on text only
10780      * @param {Mixed} s The value being converted
10781      * @return {String} The comparison value
10782      */
10783     asText : function(s){
10784         return String(s).replace(this.stripTagsRE, "");
10785     },
10786     
10787     /**
10788      * Strips all HTML tags to sort on text only - Case insensitive
10789      * @param {Mixed} s The value being converted
10790      * @return {String} The comparison value
10791      */
10792     asUCText : function(s){
10793         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10794     },
10795     
10796     /**
10797      * Case insensitive string
10798      * @param {Mixed} s The value being converted
10799      * @return {String} The comparison value
10800      */
10801     asUCString : function(s) {
10802         return String(s).toUpperCase();
10803     },
10804     
10805     /**
10806      * Date sorting
10807      * @param {Mixed} s The value being converted
10808      * @return {Number} The comparison value
10809      */
10810     asDate : function(s) {
10811         if(!s){
10812             return 0;
10813         }
10814         if(s instanceof Date){
10815             return s.getTime();
10816         }
10817         return Date.parse(String(s));
10818     },
10819     
10820     /**
10821      * Float sorting
10822      * @param {Mixed} s The value being converted
10823      * @return {Float} The comparison value
10824      */
10825     asFloat : function(s) {
10826         var val = parseFloat(String(s).replace(/,/g, ""));
10827         if(isNaN(val)) {
10828             val = 0;
10829         }
10830         return val;
10831     },
10832     
10833     /**
10834      * Integer sorting
10835      * @param {Mixed} s The value being converted
10836      * @return {Number} The comparison value
10837      */
10838     asInt : function(s) {
10839         var val = parseInt(String(s).replace(/,/g, ""));
10840         if(isNaN(val)) {
10841             val = 0;
10842         }
10843         return val;
10844     }
10845 };/*
10846  * Based on:
10847  * Ext JS Library 1.1.1
10848  * Copyright(c) 2006-2007, Ext JS, LLC.
10849  *
10850  * Originally Released Under LGPL - original licence link has changed is not relivant.
10851  *
10852  * Fork - LGPL
10853  * <script type="text/javascript">
10854  */
10855
10856 /**
10857 * @class Roo.data.Record
10858  * Instances of this class encapsulate both record <em>definition</em> information, and record
10859  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10860  * to access Records cached in an {@link Roo.data.Store} object.<br>
10861  * <p>
10862  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10863  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10864  * objects.<br>
10865  * <p>
10866  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10867  * @constructor
10868  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10869  * {@link #create}. The parameters are the same.
10870  * @param {Array} data An associative Array of data values keyed by the field name.
10871  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10872  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10873  * not specified an integer id is generated.
10874  */
10875 Roo.data.Record = function(data, id){
10876     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10877     this.data = data;
10878 };
10879
10880 /**
10881  * Generate a constructor for a specific record layout.
10882  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10883  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10884  * Each field definition object may contain the following properties: <ul>
10885  * <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,
10886  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10887  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10888  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10889  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10890  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10891  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10892  * this may be omitted.</p></li>
10893  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10894  * <ul><li>auto (Default, implies no conversion)</li>
10895  * <li>string</li>
10896  * <li>int</li>
10897  * <li>float</li>
10898  * <li>boolean</li>
10899  * <li>date</li></ul></p></li>
10900  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10901  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10902  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10903  * by the Reader into an object that will be stored in the Record. It is passed the
10904  * following parameters:<ul>
10905  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10906  * </ul></p></li>
10907  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10908  * </ul>
10909  * <br>usage:<br><pre><code>
10910 var TopicRecord = Roo.data.Record.create(
10911     {name: 'title', mapping: 'topic_title'},
10912     {name: 'author', mapping: 'username'},
10913     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10914     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10915     {name: 'lastPoster', mapping: 'user2'},
10916     {name: 'excerpt', mapping: 'post_text'}
10917 );
10918
10919 var myNewRecord = new TopicRecord({
10920     title: 'Do my job please',
10921     author: 'noobie',
10922     totalPosts: 1,
10923     lastPost: new Date(),
10924     lastPoster: 'Animal',
10925     excerpt: 'No way dude!'
10926 });
10927 myStore.add(myNewRecord);
10928 </code></pre>
10929  * @method create
10930  * @static
10931  */
10932 Roo.data.Record.create = function(o){
10933     var f = function(){
10934         f.superclass.constructor.apply(this, arguments);
10935     };
10936     Roo.extend(f, Roo.data.Record);
10937     var p = f.prototype;
10938     p.fields = new Roo.util.MixedCollection(false, function(field){
10939         return field.name;
10940     });
10941     for(var i = 0, len = o.length; i < len; i++){
10942         p.fields.add(new Roo.data.Field(o[i]));
10943     }
10944     f.getField = function(name){
10945         return p.fields.get(name);  
10946     };
10947     return f;
10948 };
10949
10950 Roo.data.Record.AUTO_ID = 1000;
10951 Roo.data.Record.EDIT = 'edit';
10952 Roo.data.Record.REJECT = 'reject';
10953 Roo.data.Record.COMMIT = 'commit';
10954
10955 Roo.data.Record.prototype = {
10956     /**
10957      * Readonly flag - true if this record has been modified.
10958      * @type Boolean
10959      */
10960     dirty : false,
10961     editing : false,
10962     error: null,
10963     modified: null,
10964
10965     // private
10966     join : function(store){
10967         this.store = store;
10968     },
10969
10970     /**
10971      * Set the named field to the specified value.
10972      * @param {String} name The name of the field to set.
10973      * @param {Object} value The value to set the field to.
10974      */
10975     set : function(name, value){
10976         if(this.data[name] == value){
10977             return;
10978         }
10979         this.dirty = true;
10980         if(!this.modified){
10981             this.modified = {};
10982         }
10983         if(typeof this.modified[name] == 'undefined'){
10984             this.modified[name] = this.data[name];
10985         }
10986         this.data[name] = value;
10987         if(!this.editing && this.store){
10988             this.store.afterEdit(this);
10989         }       
10990     },
10991
10992     /**
10993      * Get the value of the named field.
10994      * @param {String} name The name of the field to get the value of.
10995      * @return {Object} The value of the field.
10996      */
10997     get : function(name){
10998         return this.data[name]; 
10999     },
11000
11001     // private
11002     beginEdit : function(){
11003         this.editing = true;
11004         this.modified = {}; 
11005     },
11006
11007     // private
11008     cancelEdit : function(){
11009         this.editing = false;
11010         delete this.modified;
11011     },
11012
11013     // private
11014     endEdit : function(){
11015         this.editing = false;
11016         if(this.dirty && this.store){
11017             this.store.afterEdit(this);
11018         }
11019     },
11020
11021     /**
11022      * Usually called by the {@link Roo.data.Store} which owns the Record.
11023      * Rejects all changes made to the Record since either creation, or the last commit operation.
11024      * Modified fields are reverted to their original values.
11025      * <p>
11026      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11027      * of reject operations.
11028      */
11029     reject : function(){
11030         var m = this.modified;
11031         for(var n in m){
11032             if(typeof m[n] != "function"){
11033                 this.data[n] = m[n];
11034             }
11035         }
11036         this.dirty = false;
11037         delete this.modified;
11038         this.editing = false;
11039         if(this.store){
11040             this.store.afterReject(this);
11041         }
11042     },
11043
11044     /**
11045      * Usually called by the {@link Roo.data.Store} which owns the Record.
11046      * Commits all changes made to the Record since either creation, or the last commit operation.
11047      * <p>
11048      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11049      * of commit operations.
11050      */
11051     commit : function(){
11052         this.dirty = false;
11053         delete this.modified;
11054         this.editing = false;
11055         if(this.store){
11056             this.store.afterCommit(this);
11057         }
11058     },
11059
11060     // private
11061     hasError : function(){
11062         return this.error != null;
11063     },
11064
11065     // private
11066     clearError : function(){
11067         this.error = null;
11068     },
11069
11070     /**
11071      * Creates a copy of this record.
11072      * @param {String} id (optional) A new record id if you don't want to use this record's id
11073      * @return {Record}
11074      */
11075     copy : function(newId) {
11076         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11077     }
11078 };/*
11079  * Based on:
11080  * Ext JS Library 1.1.1
11081  * Copyright(c) 2006-2007, Ext JS, LLC.
11082  *
11083  * Originally Released Under LGPL - original licence link has changed is not relivant.
11084  *
11085  * Fork - LGPL
11086  * <script type="text/javascript">
11087  */
11088
11089
11090
11091 /**
11092  * @class Roo.data.Store
11093  * @extends Roo.util.Observable
11094  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11095  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11096  * <p>
11097  * 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
11098  * has no knowledge of the format of the data returned by the Proxy.<br>
11099  * <p>
11100  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11101  * instances from the data object. These records are cached and made available through accessor functions.
11102  * @constructor
11103  * Creates a new Store.
11104  * @param {Object} config A config object containing the objects needed for the Store to access data,
11105  * and read the data into Records.
11106  */
11107 Roo.data.Store = function(config){
11108     this.data = new Roo.util.MixedCollection(false);
11109     this.data.getKey = function(o){
11110         return o.id;
11111     };
11112     this.baseParams = {};
11113     // private
11114     this.paramNames = {
11115         "start" : "start",
11116         "limit" : "limit",
11117         "sort" : "sort",
11118         "dir" : "dir",
11119         "multisort" : "_multisort"
11120     };
11121
11122     if(config && config.data){
11123         this.inlineData = config.data;
11124         delete config.data;
11125     }
11126
11127     Roo.apply(this, config);
11128     
11129     if(this.reader){ // reader passed
11130         this.reader = Roo.factory(this.reader, Roo.data);
11131         this.reader.xmodule = this.xmodule || false;
11132         if(!this.recordType){
11133             this.recordType = this.reader.recordType;
11134         }
11135         if(this.reader.onMetaChange){
11136             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11137         }
11138     }
11139
11140     if(this.recordType){
11141         this.fields = this.recordType.prototype.fields;
11142     }
11143     this.modified = [];
11144
11145     this.addEvents({
11146         /**
11147          * @event datachanged
11148          * Fires when the data cache has changed, and a widget which is using this Store
11149          * as a Record cache should refresh its view.
11150          * @param {Store} this
11151          */
11152         datachanged : true,
11153         /**
11154          * @event metachange
11155          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11156          * @param {Store} this
11157          * @param {Object} meta The JSON metadata
11158          */
11159         metachange : true,
11160         /**
11161          * @event add
11162          * Fires when Records have been added to the Store
11163          * @param {Store} this
11164          * @param {Roo.data.Record[]} records The array of Records added
11165          * @param {Number} index The index at which the record(s) were added
11166          */
11167         add : true,
11168         /**
11169          * @event remove
11170          * Fires when a Record has been removed from the Store
11171          * @param {Store} this
11172          * @param {Roo.data.Record} record The Record that was removed
11173          * @param {Number} index The index at which the record was removed
11174          */
11175         remove : true,
11176         /**
11177          * @event update
11178          * Fires when a Record has been updated
11179          * @param {Store} this
11180          * @param {Roo.data.Record} record The Record that was updated
11181          * @param {String} operation The update operation being performed.  Value may be one of:
11182          * <pre><code>
11183  Roo.data.Record.EDIT
11184  Roo.data.Record.REJECT
11185  Roo.data.Record.COMMIT
11186          * </code></pre>
11187          */
11188         update : true,
11189         /**
11190          * @event clear
11191          * Fires when the data cache has been cleared.
11192          * @param {Store} this
11193          */
11194         clear : true,
11195         /**
11196          * @event beforeload
11197          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11198          * the load action will be canceled.
11199          * @param {Store} this
11200          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11201          */
11202         beforeload : true,
11203         /**
11204          * @event beforeloadadd
11205          * Fires after a new set of Records has been loaded.
11206          * @param {Store} this
11207          * @param {Roo.data.Record[]} records The Records that were loaded
11208          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11209          */
11210         beforeloadadd : true,
11211         /**
11212          * @event load
11213          * Fires after a new set of Records has been loaded, before they are added to the store.
11214          * @param {Store} this
11215          * @param {Roo.data.Record[]} records The Records that were loaded
11216          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11217          * @params {Object} return from reader
11218          */
11219         load : true,
11220         /**
11221          * @event loadexception
11222          * Fires if an exception occurs in the Proxy during loading.
11223          * Called with the signature of the Proxy's "loadexception" event.
11224          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11225          * 
11226          * @param {Proxy} 
11227          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11228          * @param {Object} load options 
11229          * @param {Object} jsonData from your request (normally this contains the Exception)
11230          */
11231         loadexception : true
11232     });
11233     
11234     if(this.proxy){
11235         this.proxy = Roo.factory(this.proxy, Roo.data);
11236         this.proxy.xmodule = this.xmodule || false;
11237         this.relayEvents(this.proxy,  ["loadexception"]);
11238     }
11239     this.sortToggle = {};
11240     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11241
11242     Roo.data.Store.superclass.constructor.call(this);
11243
11244     if(this.inlineData){
11245         this.loadData(this.inlineData);
11246         delete this.inlineData;
11247     }
11248 };
11249
11250 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11251      /**
11252     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11253     * without a remote query - used by combo/forms at present.
11254     */
11255     
11256     /**
11257     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11258     */
11259     /**
11260     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11261     */
11262     /**
11263     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11264     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11265     */
11266     /**
11267     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11268     * on any HTTP request
11269     */
11270     /**
11271     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11272     */
11273     /**
11274     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11275     */
11276     multiSort: false,
11277     /**
11278     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11279     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11280     */
11281     remoteSort : false,
11282
11283     /**
11284     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11285      * loaded or when a record is removed. (defaults to false).
11286     */
11287     pruneModifiedRecords : false,
11288
11289     // private
11290     lastOptions : null,
11291
11292     /**
11293      * Add Records to the Store and fires the add event.
11294      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11295      */
11296     add : function(records){
11297         records = [].concat(records);
11298         for(var i = 0, len = records.length; i < len; i++){
11299             records[i].join(this);
11300         }
11301         var index = this.data.length;
11302         this.data.addAll(records);
11303         this.fireEvent("add", this, records, index);
11304     },
11305
11306     /**
11307      * Remove a Record from the Store and fires the remove event.
11308      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11309      */
11310     remove : function(record){
11311         var index = this.data.indexOf(record);
11312         this.data.removeAt(index);
11313  
11314         if(this.pruneModifiedRecords){
11315             this.modified.remove(record);
11316         }
11317         this.fireEvent("remove", this, record, index);
11318     },
11319
11320     /**
11321      * Remove all Records from the Store and fires the clear event.
11322      */
11323     removeAll : function(){
11324         this.data.clear();
11325         if(this.pruneModifiedRecords){
11326             this.modified = [];
11327         }
11328         this.fireEvent("clear", this);
11329     },
11330
11331     /**
11332      * Inserts Records to the Store at the given index and fires the add event.
11333      * @param {Number} index The start index at which to insert the passed Records.
11334      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11335      */
11336     insert : function(index, records){
11337         records = [].concat(records);
11338         for(var i = 0, len = records.length; i < len; i++){
11339             this.data.insert(index, records[i]);
11340             records[i].join(this);
11341         }
11342         this.fireEvent("add", this, records, index);
11343     },
11344
11345     /**
11346      * Get the index within the cache of the passed Record.
11347      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11348      * @return {Number} The index of the passed Record. Returns -1 if not found.
11349      */
11350     indexOf : function(record){
11351         return this.data.indexOf(record);
11352     },
11353
11354     /**
11355      * Get the index within the cache of the Record with the passed id.
11356      * @param {String} id The id of the Record to find.
11357      * @return {Number} The index of the Record. Returns -1 if not found.
11358      */
11359     indexOfId : function(id){
11360         return this.data.indexOfKey(id);
11361     },
11362
11363     /**
11364      * Get the Record with the specified id.
11365      * @param {String} id The id of the Record to find.
11366      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11367      */
11368     getById : function(id){
11369         return this.data.key(id);
11370     },
11371
11372     /**
11373      * Get the Record at the specified index.
11374      * @param {Number} index The index of the Record to find.
11375      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11376      */
11377     getAt : function(index){
11378         return this.data.itemAt(index);
11379     },
11380
11381     /**
11382      * Returns a range of Records between specified indices.
11383      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11384      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11385      * @return {Roo.data.Record[]} An array of Records
11386      */
11387     getRange : function(start, end){
11388         return this.data.getRange(start, end);
11389     },
11390
11391     // private
11392     storeOptions : function(o){
11393         o = Roo.apply({}, o);
11394         delete o.callback;
11395         delete o.scope;
11396         this.lastOptions = o;
11397     },
11398
11399     /**
11400      * Loads the Record cache from the configured Proxy using the configured Reader.
11401      * <p>
11402      * If using remote paging, then the first load call must specify the <em>start</em>
11403      * and <em>limit</em> properties in the options.params property to establish the initial
11404      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11405      * <p>
11406      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11407      * and this call will return before the new data has been loaded. Perform any post-processing
11408      * in a callback function, or in a "load" event handler.</strong>
11409      * <p>
11410      * @param {Object} options An object containing properties which control loading options:<ul>
11411      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11412      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11413      * passed the following arguments:<ul>
11414      * <li>r : Roo.data.Record[]</li>
11415      * <li>options: Options object from the load call</li>
11416      * <li>success: Boolean success indicator</li></ul></li>
11417      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11418      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11419      * </ul>
11420      */
11421     load : function(options){
11422         options = options || {};
11423         if(this.fireEvent("beforeload", this, options) !== false){
11424             this.storeOptions(options);
11425             var p = Roo.apply(options.params || {}, this.baseParams);
11426             // if meta was not loaded from remote source.. try requesting it.
11427             if (!this.reader.metaFromRemote) {
11428                 p._requestMeta = 1;
11429             }
11430             if(this.sortInfo && this.remoteSort){
11431                 var pn = this.paramNames;
11432                 p[pn["sort"]] = this.sortInfo.field;
11433                 p[pn["dir"]] = this.sortInfo.direction;
11434             }
11435             if (this.multiSort) {
11436                 var pn = this.paramNames;
11437                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11438             }
11439             
11440             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11441         }
11442     },
11443
11444     /**
11445      * Reloads the Record cache from the configured Proxy using the configured Reader and
11446      * the options from the last load operation performed.
11447      * @param {Object} options (optional) An object containing properties which may override the options
11448      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11449      * the most recently used options are reused).
11450      */
11451     reload : function(options){
11452         this.load(Roo.applyIf(options||{}, this.lastOptions));
11453     },
11454
11455     // private
11456     // Called as a callback by the Reader during a load operation.
11457     loadRecords : function(o, options, success){
11458         if(!o || success === false){
11459             if(success !== false){
11460                 this.fireEvent("load", this, [], options, o);
11461             }
11462             if(options.callback){
11463                 options.callback.call(options.scope || this, [], options, false);
11464             }
11465             return;
11466         }
11467         // if data returned failure - throw an exception.
11468         if (o.success === false) {
11469             // show a message if no listener is registered.
11470             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11471                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11472             }
11473             // loadmask wil be hooked into this..
11474             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11475             return;
11476         }
11477         var r = o.records, t = o.totalRecords || r.length;
11478         
11479         this.fireEvent("beforeloadadd", this, r, options, o);
11480         
11481         if(!options || options.add !== true){
11482             if(this.pruneModifiedRecords){
11483                 this.modified = [];
11484             }
11485             for(var i = 0, len = r.length; i < len; i++){
11486                 r[i].join(this);
11487             }
11488             if(this.snapshot){
11489                 this.data = this.snapshot;
11490                 delete this.snapshot;
11491             }
11492             this.data.clear();
11493             this.data.addAll(r);
11494             this.totalLength = t;
11495             this.applySort();
11496             this.fireEvent("datachanged", this);
11497         }else{
11498             this.totalLength = Math.max(t, this.data.length+r.length);
11499             this.add(r);
11500         }
11501         
11502         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11503                 
11504             var e = new Roo.data.Record({});
11505
11506             e.set(this.parent.displayField, this.parent.emptyTitle);
11507             e.set(this.parent.valueField, '');
11508
11509             this.insert(0, e);
11510         }
11511             
11512         this.fireEvent("load", this, r, options, o);
11513         if(options.callback){
11514             options.callback.call(options.scope || this, r, options, true);
11515         }
11516     },
11517
11518
11519     /**
11520      * Loads data from a passed data block. A Reader which understands the format of the data
11521      * must have been configured in the constructor.
11522      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11523      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11524      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11525      */
11526     loadData : function(o, append){
11527         var r = this.reader.readRecords(o);
11528         this.loadRecords(r, {add: append}, true);
11529     },
11530
11531     /**
11532      * Gets the number of cached records.
11533      * <p>
11534      * <em>If using paging, this may not be the total size of the dataset. If the data object
11535      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11536      * the data set size</em>
11537      */
11538     getCount : function(){
11539         return this.data.length || 0;
11540     },
11541
11542     /**
11543      * Gets the total number of records in the dataset as returned by the server.
11544      * <p>
11545      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11546      * the dataset size</em>
11547      */
11548     getTotalCount : function(){
11549         return this.totalLength || 0;
11550     },
11551
11552     /**
11553      * Returns the sort state of the Store as an object with two properties:
11554      * <pre><code>
11555  field {String} The name of the field by which the Records are sorted
11556  direction {String} The sort order, "ASC" or "DESC"
11557      * </code></pre>
11558      */
11559     getSortState : function(){
11560         return this.sortInfo;
11561     },
11562
11563     // private
11564     applySort : function(){
11565         if(this.sortInfo && !this.remoteSort){
11566             var s = this.sortInfo, f = s.field;
11567             var st = this.fields.get(f).sortType;
11568             var fn = function(r1, r2){
11569                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11570                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11571             };
11572             this.data.sort(s.direction, fn);
11573             if(this.snapshot && this.snapshot != this.data){
11574                 this.snapshot.sort(s.direction, fn);
11575             }
11576         }
11577     },
11578
11579     /**
11580      * Sets the default sort column and order to be used by the next load operation.
11581      * @param {String} fieldName The name of the field to sort by.
11582      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11583      */
11584     setDefaultSort : function(field, dir){
11585         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11586     },
11587
11588     /**
11589      * Sort the Records.
11590      * If remote sorting is used, the sort is performed on the server, and the cache is
11591      * reloaded. If local sorting is used, the cache is sorted internally.
11592      * @param {String} fieldName The name of the field to sort by.
11593      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11594      */
11595     sort : function(fieldName, dir){
11596         var f = this.fields.get(fieldName);
11597         if(!dir){
11598             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11599             
11600             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11601                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11602             }else{
11603                 dir = f.sortDir;
11604             }
11605         }
11606         this.sortToggle[f.name] = dir;
11607         this.sortInfo = {field: f.name, direction: dir};
11608         if(!this.remoteSort){
11609             this.applySort();
11610             this.fireEvent("datachanged", this);
11611         }else{
11612             this.load(this.lastOptions);
11613         }
11614     },
11615
11616     /**
11617      * Calls the specified function for each of the Records in the cache.
11618      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11619      * Returning <em>false</em> aborts and exits the iteration.
11620      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11621      */
11622     each : function(fn, scope){
11623         this.data.each(fn, scope);
11624     },
11625
11626     /**
11627      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11628      * (e.g., during paging).
11629      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11630      */
11631     getModifiedRecords : function(){
11632         return this.modified;
11633     },
11634
11635     // private
11636     createFilterFn : function(property, value, anyMatch){
11637         if(!value.exec){ // not a regex
11638             value = String(value);
11639             if(value.length == 0){
11640                 return false;
11641             }
11642             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11643         }
11644         return function(r){
11645             return value.test(r.data[property]);
11646         };
11647     },
11648
11649     /**
11650      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11651      * @param {String} property A field on your records
11652      * @param {Number} start The record index to start at (defaults to 0)
11653      * @param {Number} end The last record index to include (defaults to length - 1)
11654      * @return {Number} The sum
11655      */
11656     sum : function(property, start, end){
11657         var rs = this.data.items, v = 0;
11658         start = start || 0;
11659         end = (end || end === 0) ? end : rs.length-1;
11660
11661         for(var i = start; i <= end; i++){
11662             v += (rs[i].data[property] || 0);
11663         }
11664         return v;
11665     },
11666
11667     /**
11668      * Filter the records by a specified property.
11669      * @param {String} field A field on your records
11670      * @param {String/RegExp} value Either a string that the field
11671      * should start with or a RegExp to test against the field
11672      * @param {Boolean} anyMatch True to match any part not just the beginning
11673      */
11674     filter : function(property, value, anyMatch){
11675         var fn = this.createFilterFn(property, value, anyMatch);
11676         return fn ? this.filterBy(fn) : this.clearFilter();
11677     },
11678
11679     /**
11680      * Filter by a function. The specified function will be called with each
11681      * record in this data source. If the function returns true the record is included,
11682      * otherwise it is filtered.
11683      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11684      * @param {Object} scope (optional) The scope of the function (defaults to this)
11685      */
11686     filterBy : function(fn, scope){
11687         this.snapshot = this.snapshot || this.data;
11688         this.data = this.queryBy(fn, scope||this);
11689         this.fireEvent("datachanged", this);
11690     },
11691
11692     /**
11693      * Query the records by a specified property.
11694      * @param {String} field A field on your records
11695      * @param {String/RegExp} value Either a string that the field
11696      * should start with or a RegExp to test against the field
11697      * @param {Boolean} anyMatch True to match any part not just the beginning
11698      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11699      */
11700     query : function(property, value, anyMatch){
11701         var fn = this.createFilterFn(property, value, anyMatch);
11702         return fn ? this.queryBy(fn) : this.data.clone();
11703     },
11704
11705     /**
11706      * Query by a function. The specified function will be called with each
11707      * record in this data source. If the function returns true the record is included
11708      * in the results.
11709      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11710      * @param {Object} scope (optional) The scope of the function (defaults to this)
11711       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11712      **/
11713     queryBy : function(fn, scope){
11714         var data = this.snapshot || this.data;
11715         return data.filterBy(fn, scope||this);
11716     },
11717
11718     /**
11719      * Collects unique values for a particular dataIndex from this store.
11720      * @param {String} dataIndex The property to collect
11721      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11722      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11723      * @return {Array} An array of the unique values
11724      **/
11725     collect : function(dataIndex, allowNull, bypassFilter){
11726         var d = (bypassFilter === true && this.snapshot) ?
11727                 this.snapshot.items : this.data.items;
11728         var v, sv, r = [], l = {};
11729         for(var i = 0, len = d.length; i < len; i++){
11730             v = d[i].data[dataIndex];
11731             sv = String(v);
11732             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11733                 l[sv] = true;
11734                 r[r.length] = v;
11735             }
11736         }
11737         return r;
11738     },
11739
11740     /**
11741      * Revert to a view of the Record cache with no filtering applied.
11742      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11743      */
11744     clearFilter : function(suppressEvent){
11745         if(this.snapshot && this.snapshot != this.data){
11746             this.data = this.snapshot;
11747             delete this.snapshot;
11748             if(suppressEvent !== true){
11749                 this.fireEvent("datachanged", this);
11750             }
11751         }
11752     },
11753
11754     // private
11755     afterEdit : function(record){
11756         if(this.modified.indexOf(record) == -1){
11757             this.modified.push(record);
11758         }
11759         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11760     },
11761     
11762     // private
11763     afterReject : function(record){
11764         this.modified.remove(record);
11765         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11766     },
11767
11768     // private
11769     afterCommit : function(record){
11770         this.modified.remove(record);
11771         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11772     },
11773
11774     /**
11775      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11776      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11777      */
11778     commitChanges : function(){
11779         var m = this.modified.slice(0);
11780         this.modified = [];
11781         for(var i = 0, len = m.length; i < len; i++){
11782             m[i].commit();
11783         }
11784     },
11785
11786     /**
11787      * Cancel outstanding changes on all changed records.
11788      */
11789     rejectChanges : function(){
11790         var m = this.modified.slice(0);
11791         this.modified = [];
11792         for(var i = 0, len = m.length; i < len; i++){
11793             m[i].reject();
11794         }
11795     },
11796
11797     onMetaChange : function(meta, rtype, o){
11798         this.recordType = rtype;
11799         this.fields = rtype.prototype.fields;
11800         delete this.snapshot;
11801         this.sortInfo = meta.sortInfo || this.sortInfo;
11802         this.modified = [];
11803         this.fireEvent('metachange', this, this.reader.meta);
11804     },
11805     
11806     moveIndex : function(data, type)
11807     {
11808         var index = this.indexOf(data);
11809         
11810         var newIndex = index + type;
11811         
11812         this.remove(data);
11813         
11814         this.insert(newIndex, data);
11815         
11816     }
11817 });/*
11818  * Based on:
11819  * Ext JS Library 1.1.1
11820  * Copyright(c) 2006-2007, Ext JS, LLC.
11821  *
11822  * Originally Released Under LGPL - original licence link has changed is not relivant.
11823  *
11824  * Fork - LGPL
11825  * <script type="text/javascript">
11826  */
11827
11828 /**
11829  * @class Roo.data.SimpleStore
11830  * @extends Roo.data.Store
11831  * Small helper class to make creating Stores from Array data easier.
11832  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11833  * @cfg {Array} fields An array of field definition objects, or field name strings.
11834  * @cfg {Array} data The multi-dimensional array of data
11835  * @constructor
11836  * @param {Object} config
11837  */
11838 Roo.data.SimpleStore = function(config){
11839     Roo.data.SimpleStore.superclass.constructor.call(this, {
11840         isLocal : true,
11841         reader: new Roo.data.ArrayReader({
11842                 id: config.id
11843             },
11844             Roo.data.Record.create(config.fields)
11845         ),
11846         proxy : new Roo.data.MemoryProxy(config.data)
11847     });
11848     this.load();
11849 };
11850 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11851  * Based on:
11852  * Ext JS Library 1.1.1
11853  * Copyright(c) 2006-2007, Ext JS, LLC.
11854  *
11855  * Originally Released Under LGPL - original licence link has changed is not relivant.
11856  *
11857  * Fork - LGPL
11858  * <script type="text/javascript">
11859  */
11860
11861 /**
11862 /**
11863  * @extends Roo.data.Store
11864  * @class Roo.data.JsonStore
11865  * Small helper class to make creating Stores for JSON data easier. <br/>
11866 <pre><code>
11867 var store = new Roo.data.JsonStore({
11868     url: 'get-images.php',
11869     root: 'images',
11870     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11871 });
11872 </code></pre>
11873  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11874  * JsonReader and HttpProxy (unless inline data is provided).</b>
11875  * @cfg {Array} fields An array of field definition objects, or field name strings.
11876  * @constructor
11877  * @param {Object} config
11878  */
11879 Roo.data.JsonStore = function(c){
11880     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11881         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11882         reader: new Roo.data.JsonReader(c, c.fields)
11883     }));
11884 };
11885 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11886  * Based on:
11887  * Ext JS Library 1.1.1
11888  * Copyright(c) 2006-2007, Ext JS, LLC.
11889  *
11890  * Originally Released Under LGPL - original licence link has changed is not relivant.
11891  *
11892  * Fork - LGPL
11893  * <script type="text/javascript">
11894  */
11895
11896  
11897 Roo.data.Field = function(config){
11898     if(typeof config == "string"){
11899         config = {name: config};
11900     }
11901     Roo.apply(this, config);
11902     
11903     if(!this.type){
11904         this.type = "auto";
11905     }
11906     
11907     var st = Roo.data.SortTypes;
11908     // named sortTypes are supported, here we look them up
11909     if(typeof this.sortType == "string"){
11910         this.sortType = st[this.sortType];
11911     }
11912     
11913     // set default sortType for strings and dates
11914     if(!this.sortType){
11915         switch(this.type){
11916             case "string":
11917                 this.sortType = st.asUCString;
11918                 break;
11919             case "date":
11920                 this.sortType = st.asDate;
11921                 break;
11922             default:
11923                 this.sortType = st.none;
11924         }
11925     }
11926
11927     // define once
11928     var stripRe = /[\$,%]/g;
11929
11930     // prebuilt conversion function for this field, instead of
11931     // switching every time we're reading a value
11932     if(!this.convert){
11933         var cv, dateFormat = this.dateFormat;
11934         switch(this.type){
11935             case "":
11936             case "auto":
11937             case undefined:
11938                 cv = function(v){ return v; };
11939                 break;
11940             case "string":
11941                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11942                 break;
11943             case "int":
11944                 cv = function(v){
11945                     return v !== undefined && v !== null && v !== '' ?
11946                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11947                     };
11948                 break;
11949             case "float":
11950                 cv = function(v){
11951                     return v !== undefined && v !== null && v !== '' ?
11952                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11953                     };
11954                 break;
11955             case "bool":
11956             case "boolean":
11957                 cv = function(v){ return v === true || v === "true" || v == 1; };
11958                 break;
11959             case "date":
11960                 cv = function(v){
11961                     if(!v){
11962                         return '';
11963                     }
11964                     if(v instanceof Date){
11965                         return v;
11966                     }
11967                     if(dateFormat){
11968                         if(dateFormat == "timestamp"){
11969                             return new Date(v*1000);
11970                         }
11971                         return Date.parseDate(v, dateFormat);
11972                     }
11973                     var parsed = Date.parse(v);
11974                     return parsed ? new Date(parsed) : null;
11975                 };
11976              break;
11977             
11978         }
11979         this.convert = cv;
11980     }
11981 };
11982
11983 Roo.data.Field.prototype = {
11984     dateFormat: null,
11985     defaultValue: "",
11986     mapping: null,
11987     sortType : null,
11988     sortDir : "ASC"
11989 };/*
11990  * Based on:
11991  * Ext JS Library 1.1.1
11992  * Copyright(c) 2006-2007, Ext JS, LLC.
11993  *
11994  * Originally Released Under LGPL - original licence link has changed is not relivant.
11995  *
11996  * Fork - LGPL
11997  * <script type="text/javascript">
11998  */
11999  
12000 // Base class for reading structured data from a data source.  This class is intended to be
12001 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12002
12003 /**
12004  * @class Roo.data.DataReader
12005  * Base class for reading structured data from a data source.  This class is intended to be
12006  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12007  */
12008
12009 Roo.data.DataReader = function(meta, recordType){
12010     
12011     this.meta = meta;
12012     
12013     this.recordType = recordType instanceof Array ? 
12014         Roo.data.Record.create(recordType) : recordType;
12015 };
12016
12017 Roo.data.DataReader.prototype = {
12018      /**
12019      * Create an empty record
12020      * @param {Object} data (optional) - overlay some values
12021      * @return {Roo.data.Record} record created.
12022      */
12023     newRow :  function(d) {
12024         var da =  {};
12025         this.recordType.prototype.fields.each(function(c) {
12026             switch( c.type) {
12027                 case 'int' : da[c.name] = 0; break;
12028                 case 'date' : da[c.name] = new Date(); break;
12029                 case 'float' : da[c.name] = 0.0; break;
12030                 case 'boolean' : da[c.name] = false; break;
12031                 default : da[c.name] = ""; break;
12032             }
12033             
12034         });
12035         return new this.recordType(Roo.apply(da, d));
12036     }
12037     
12038 };/*
12039  * Based on:
12040  * Ext JS Library 1.1.1
12041  * Copyright(c) 2006-2007, Ext JS, LLC.
12042  *
12043  * Originally Released Under LGPL - original licence link has changed is not relivant.
12044  *
12045  * Fork - LGPL
12046  * <script type="text/javascript">
12047  */
12048
12049 /**
12050  * @class Roo.data.DataProxy
12051  * @extends Roo.data.Observable
12052  * This class is an abstract base class for implementations which provide retrieval of
12053  * unformatted data objects.<br>
12054  * <p>
12055  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12056  * (of the appropriate type which knows how to parse the data object) to provide a block of
12057  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12058  * <p>
12059  * Custom implementations must implement the load method as described in
12060  * {@link Roo.data.HttpProxy#load}.
12061  */
12062 Roo.data.DataProxy = function(){
12063     this.addEvents({
12064         /**
12065          * @event beforeload
12066          * Fires before a network request is made to retrieve a data object.
12067          * @param {Object} This DataProxy object.
12068          * @param {Object} params The params parameter to the load function.
12069          */
12070         beforeload : true,
12071         /**
12072          * @event load
12073          * Fires before the load method's callback is called.
12074          * @param {Object} This DataProxy object.
12075          * @param {Object} o The data object.
12076          * @param {Object} arg The callback argument object passed to the load function.
12077          */
12078         load : true,
12079         /**
12080          * @event loadexception
12081          * Fires if an Exception occurs during data retrieval.
12082          * @param {Object} This DataProxy object.
12083          * @param {Object} o The data object.
12084          * @param {Object} arg The callback argument object passed to the load function.
12085          * @param {Object} e The Exception.
12086          */
12087         loadexception : true
12088     });
12089     Roo.data.DataProxy.superclass.constructor.call(this);
12090 };
12091
12092 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12093
12094     /**
12095      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12096      */
12097 /*
12098  * Based on:
12099  * Ext JS Library 1.1.1
12100  * Copyright(c) 2006-2007, Ext JS, LLC.
12101  *
12102  * Originally Released Under LGPL - original licence link has changed is not relivant.
12103  *
12104  * Fork - LGPL
12105  * <script type="text/javascript">
12106  */
12107 /**
12108  * @class Roo.data.MemoryProxy
12109  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12110  * to the Reader when its load method is called.
12111  * @constructor
12112  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12113  */
12114 Roo.data.MemoryProxy = function(data){
12115     if (data.data) {
12116         data = data.data;
12117     }
12118     Roo.data.MemoryProxy.superclass.constructor.call(this);
12119     this.data = data;
12120 };
12121
12122 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12123     
12124     /**
12125      * Load data from the requested source (in this case an in-memory
12126      * data object passed to the constructor), read the data object into
12127      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12128      * process that block using the passed callback.
12129      * @param {Object} params This parameter is not used by the MemoryProxy class.
12130      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12131      * object into a block of Roo.data.Records.
12132      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12133      * The function must be passed <ul>
12134      * <li>The Record block object</li>
12135      * <li>The "arg" argument from the load function</li>
12136      * <li>A boolean success indicator</li>
12137      * </ul>
12138      * @param {Object} scope The scope in which to call the callback
12139      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12140      */
12141     load : function(params, reader, callback, scope, arg){
12142         params = params || {};
12143         var result;
12144         try {
12145             result = reader.readRecords(this.data);
12146         }catch(e){
12147             this.fireEvent("loadexception", this, arg, null, e);
12148             callback.call(scope, null, arg, false);
12149             return;
12150         }
12151         callback.call(scope, result, arg, true);
12152     },
12153     
12154     // private
12155     update : function(params, records){
12156         
12157     }
12158 });/*
12159  * Based on:
12160  * Ext JS Library 1.1.1
12161  * Copyright(c) 2006-2007, Ext JS, LLC.
12162  *
12163  * Originally Released Under LGPL - original licence link has changed is not relivant.
12164  *
12165  * Fork - LGPL
12166  * <script type="text/javascript">
12167  */
12168 /**
12169  * @class Roo.data.HttpProxy
12170  * @extends Roo.data.DataProxy
12171  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12172  * configured to reference a certain URL.<br><br>
12173  * <p>
12174  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12175  * from which the running page was served.<br><br>
12176  * <p>
12177  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12178  * <p>
12179  * Be aware that to enable the browser to parse an XML document, the server must set
12180  * the Content-Type header in the HTTP response to "text/xml".
12181  * @constructor
12182  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12183  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12184  * will be used to make the request.
12185  */
12186 Roo.data.HttpProxy = function(conn){
12187     Roo.data.HttpProxy.superclass.constructor.call(this);
12188     // is conn a conn config or a real conn?
12189     this.conn = conn;
12190     this.useAjax = !conn || !conn.events;
12191   
12192 };
12193
12194 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12195     // thse are take from connection...
12196     
12197     /**
12198      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12199      */
12200     /**
12201      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12202      * extra parameters to each request made by this object. (defaults to undefined)
12203      */
12204     /**
12205      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12206      *  to each request made by this object. (defaults to undefined)
12207      */
12208     /**
12209      * @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)
12210      */
12211     /**
12212      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12213      */
12214      /**
12215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12216      * @type Boolean
12217      */
12218   
12219
12220     /**
12221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12222      * @type Boolean
12223      */
12224     /**
12225      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12226      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12227      * a finer-grained basis than the DataProxy events.
12228      */
12229     getConnection : function(){
12230         return this.useAjax ? Roo.Ajax : this.conn;
12231     },
12232
12233     /**
12234      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12235      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12236      * process that block using the passed callback.
12237      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12238      * for the request to the remote server.
12239      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12240      * object into a block of Roo.data.Records.
12241      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12242      * The function must be passed <ul>
12243      * <li>The Record block object</li>
12244      * <li>The "arg" argument from the load function</li>
12245      * <li>A boolean success indicator</li>
12246      * </ul>
12247      * @param {Object} scope The scope in which to call the callback
12248      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12249      */
12250     load : function(params, reader, callback, scope, arg){
12251         if(this.fireEvent("beforeload", this, params) !== false){
12252             var  o = {
12253                 params : params || {},
12254                 request: {
12255                     callback : callback,
12256                     scope : scope,
12257                     arg : arg
12258                 },
12259                 reader: reader,
12260                 callback : this.loadResponse,
12261                 scope: this
12262             };
12263             if(this.useAjax){
12264                 Roo.applyIf(o, this.conn);
12265                 if(this.activeRequest){
12266                     Roo.Ajax.abort(this.activeRequest);
12267                 }
12268                 this.activeRequest = Roo.Ajax.request(o);
12269             }else{
12270                 this.conn.request(o);
12271             }
12272         }else{
12273             callback.call(scope||this, null, arg, false);
12274         }
12275     },
12276
12277     // private
12278     loadResponse : function(o, success, response){
12279         delete this.activeRequest;
12280         if(!success){
12281             this.fireEvent("loadexception", this, o, response);
12282             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12283             return;
12284         }
12285         var result;
12286         try {
12287             result = o.reader.read(response);
12288         }catch(e){
12289             this.fireEvent("loadexception", this, o, response, e);
12290             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12291             return;
12292         }
12293         
12294         this.fireEvent("load", this, o, o.request.arg);
12295         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12296     },
12297
12298     // private
12299     update : function(dataSet){
12300
12301     },
12302
12303     // private
12304     updateResponse : function(dataSet){
12305
12306     }
12307 });/*
12308  * Based on:
12309  * Ext JS Library 1.1.1
12310  * Copyright(c) 2006-2007, Ext JS, LLC.
12311  *
12312  * Originally Released Under LGPL - original licence link has changed is not relivant.
12313  *
12314  * Fork - LGPL
12315  * <script type="text/javascript">
12316  */
12317
12318 /**
12319  * @class Roo.data.ScriptTagProxy
12320  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12321  * other than the originating domain of the running page.<br><br>
12322  * <p>
12323  * <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
12324  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12325  * <p>
12326  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12327  * source code that is used as the source inside a &lt;script> tag.<br><br>
12328  * <p>
12329  * In order for the browser to process the returned data, the server must wrap the data object
12330  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12331  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12332  * depending on whether the callback name was passed:
12333  * <p>
12334  * <pre><code>
12335 boolean scriptTag = false;
12336 String cb = request.getParameter("callback");
12337 if (cb != null) {
12338     scriptTag = true;
12339     response.setContentType("text/javascript");
12340 } else {
12341     response.setContentType("application/x-json");
12342 }
12343 Writer out = response.getWriter();
12344 if (scriptTag) {
12345     out.write(cb + "(");
12346 }
12347 out.print(dataBlock.toJsonString());
12348 if (scriptTag) {
12349     out.write(");");
12350 }
12351 </pre></code>
12352  *
12353  * @constructor
12354  * @param {Object} config A configuration object.
12355  */
12356 Roo.data.ScriptTagProxy = function(config){
12357     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12358     Roo.apply(this, config);
12359     this.head = document.getElementsByTagName("head")[0];
12360 };
12361
12362 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12363
12364 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12365     /**
12366      * @cfg {String} url The URL from which to request the data object.
12367      */
12368     /**
12369      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12370      */
12371     timeout : 30000,
12372     /**
12373      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12374      * the server the name of the callback function set up by the load call to process the returned data object.
12375      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12376      * javascript output which calls this named function passing the data object as its only parameter.
12377      */
12378     callbackParam : "callback",
12379     /**
12380      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12381      * name to the request.
12382      */
12383     nocache : true,
12384
12385     /**
12386      * Load data from the configured URL, read the data object into
12387      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12388      * process that block using the passed callback.
12389      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12390      * for the request to the remote server.
12391      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12392      * object into a block of Roo.data.Records.
12393      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12394      * The function must be passed <ul>
12395      * <li>The Record block object</li>
12396      * <li>The "arg" argument from the load function</li>
12397      * <li>A boolean success indicator</li>
12398      * </ul>
12399      * @param {Object} scope The scope in which to call the callback
12400      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12401      */
12402     load : function(params, reader, callback, scope, arg){
12403         if(this.fireEvent("beforeload", this, params) !== false){
12404
12405             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12406
12407             var url = this.url;
12408             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12409             if(this.nocache){
12410                 url += "&_dc=" + (new Date().getTime());
12411             }
12412             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12413             var trans = {
12414                 id : transId,
12415                 cb : "stcCallback"+transId,
12416                 scriptId : "stcScript"+transId,
12417                 params : params,
12418                 arg : arg,
12419                 url : url,
12420                 callback : callback,
12421                 scope : scope,
12422                 reader : reader
12423             };
12424             var conn = this;
12425
12426             window[trans.cb] = function(o){
12427                 conn.handleResponse(o, trans);
12428             };
12429
12430             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12431
12432             if(this.autoAbort !== false){
12433                 this.abort();
12434             }
12435
12436             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12437
12438             var script = document.createElement("script");
12439             script.setAttribute("src", url);
12440             script.setAttribute("type", "text/javascript");
12441             script.setAttribute("id", trans.scriptId);
12442             this.head.appendChild(script);
12443
12444             this.trans = trans;
12445         }else{
12446             callback.call(scope||this, null, arg, false);
12447         }
12448     },
12449
12450     // private
12451     isLoading : function(){
12452         return this.trans ? true : false;
12453     },
12454
12455     /**
12456      * Abort the current server request.
12457      */
12458     abort : function(){
12459         if(this.isLoading()){
12460             this.destroyTrans(this.trans);
12461         }
12462     },
12463
12464     // private
12465     destroyTrans : function(trans, isLoaded){
12466         this.head.removeChild(document.getElementById(trans.scriptId));
12467         clearTimeout(trans.timeoutId);
12468         if(isLoaded){
12469             window[trans.cb] = undefined;
12470             try{
12471                 delete window[trans.cb];
12472             }catch(e){}
12473         }else{
12474             // if hasn't been loaded, wait for load to remove it to prevent script error
12475             window[trans.cb] = function(){
12476                 window[trans.cb] = undefined;
12477                 try{
12478                     delete window[trans.cb];
12479                 }catch(e){}
12480             };
12481         }
12482     },
12483
12484     // private
12485     handleResponse : function(o, trans){
12486         this.trans = false;
12487         this.destroyTrans(trans, true);
12488         var result;
12489         try {
12490             result = trans.reader.readRecords(o);
12491         }catch(e){
12492             this.fireEvent("loadexception", this, o, trans.arg, e);
12493             trans.callback.call(trans.scope||window, null, trans.arg, false);
12494             return;
12495         }
12496         this.fireEvent("load", this, o, trans.arg);
12497         trans.callback.call(trans.scope||window, result, trans.arg, true);
12498     },
12499
12500     // private
12501     handleFailure : function(trans){
12502         this.trans = false;
12503         this.destroyTrans(trans, false);
12504         this.fireEvent("loadexception", this, null, trans.arg);
12505         trans.callback.call(trans.scope||window, null, trans.arg, false);
12506     }
12507 });/*
12508  * Based on:
12509  * Ext JS Library 1.1.1
12510  * Copyright(c) 2006-2007, Ext JS, LLC.
12511  *
12512  * Originally Released Under LGPL - original licence link has changed is not relivant.
12513  *
12514  * Fork - LGPL
12515  * <script type="text/javascript">
12516  */
12517
12518 /**
12519  * @class Roo.data.JsonReader
12520  * @extends Roo.data.DataReader
12521  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12522  * based on mappings in a provided Roo.data.Record constructor.
12523  * 
12524  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12525  * in the reply previously. 
12526  * 
12527  * <p>
12528  * Example code:
12529  * <pre><code>
12530 var RecordDef = Roo.data.Record.create([
12531     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12532     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12533 ]);
12534 var myReader = new Roo.data.JsonReader({
12535     totalProperty: "results",    // The property which contains the total dataset size (optional)
12536     root: "rows",                // The property which contains an Array of row objects
12537     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12538 }, RecordDef);
12539 </code></pre>
12540  * <p>
12541  * This would consume a JSON file like this:
12542  * <pre><code>
12543 { 'results': 2, 'rows': [
12544     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12545     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12546 }
12547 </code></pre>
12548  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12549  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12550  * paged from the remote server.
12551  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12552  * @cfg {String} root name of the property which contains the Array of row objects.
12553  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12554  * @cfg {Array} fields Array of field definition objects
12555  * @constructor
12556  * Create a new JsonReader
12557  * @param {Object} meta Metadata configuration options
12558  * @param {Object} recordType Either an Array of field definition objects,
12559  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12560  */
12561 Roo.data.JsonReader = function(meta, recordType){
12562     
12563     meta = meta || {};
12564     // set some defaults:
12565     Roo.applyIf(meta, {
12566         totalProperty: 'total',
12567         successProperty : 'success',
12568         root : 'data',
12569         id : 'id'
12570     });
12571     
12572     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12573 };
12574 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12575     
12576     /**
12577      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12578      * Used by Store query builder to append _requestMeta to params.
12579      * 
12580      */
12581     metaFromRemote : false,
12582     /**
12583      * This method is only used by a DataProxy which has retrieved data from a remote server.
12584      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12585      * @return {Object} data A data block which is used by an Roo.data.Store object as
12586      * a cache of Roo.data.Records.
12587      */
12588     read : function(response){
12589         var json = response.responseText;
12590        
12591         var o = /* eval:var:o */ eval("("+json+")");
12592         if(!o) {
12593             throw {message: "JsonReader.read: Json object not found"};
12594         }
12595         
12596         if(o.metaData){
12597             
12598             delete this.ef;
12599             this.metaFromRemote = true;
12600             this.meta = o.metaData;
12601             this.recordType = Roo.data.Record.create(o.metaData.fields);
12602             this.onMetaChange(this.meta, this.recordType, o);
12603         }
12604         return this.readRecords(o);
12605     },
12606
12607     // private function a store will implement
12608     onMetaChange : function(meta, recordType, o){
12609
12610     },
12611
12612     /**
12613          * @ignore
12614          */
12615     simpleAccess: function(obj, subsc) {
12616         return obj[subsc];
12617     },
12618
12619         /**
12620          * @ignore
12621          */
12622     getJsonAccessor: function(){
12623         var re = /[\[\.]/;
12624         return function(expr) {
12625             try {
12626                 return(re.test(expr))
12627                     ? new Function("obj", "return obj." + expr)
12628                     : function(obj){
12629                         return obj[expr];
12630                     };
12631             } catch(e){}
12632             return Roo.emptyFn;
12633         };
12634     }(),
12635
12636     /**
12637      * Create a data block containing Roo.data.Records from an XML document.
12638      * @param {Object} o An object which contains an Array of row objects in the property specified
12639      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12640      * which contains the total size of the dataset.
12641      * @return {Object} data A data block which is used by an Roo.data.Store object as
12642      * a cache of Roo.data.Records.
12643      */
12644     readRecords : function(o){
12645         /**
12646          * After any data loads, the raw JSON data is available for further custom processing.
12647          * @type Object
12648          */
12649         this.o = o;
12650         var s = this.meta, Record = this.recordType,
12651             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12652
12653 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12654         if (!this.ef) {
12655             if(s.totalProperty) {
12656                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12657                 }
12658                 if(s.successProperty) {
12659                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12660                 }
12661                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12662                 if (s.id) {
12663                         var g = this.getJsonAccessor(s.id);
12664                         this.getId = function(rec) {
12665                                 var r = g(rec);  
12666                                 return (r === undefined || r === "") ? null : r;
12667                         };
12668                 } else {
12669                         this.getId = function(){return null;};
12670                 }
12671             this.ef = [];
12672             for(var jj = 0; jj < fl; jj++){
12673                 f = fi[jj];
12674                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12675                 this.ef[jj] = this.getJsonAccessor(map);
12676             }
12677         }
12678
12679         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12680         if(s.totalProperty){
12681             var vt = parseInt(this.getTotal(o), 10);
12682             if(!isNaN(vt)){
12683                 totalRecords = vt;
12684             }
12685         }
12686         if(s.successProperty){
12687             var vs = this.getSuccess(o);
12688             if(vs === false || vs === 'false'){
12689                 success = false;
12690             }
12691         }
12692         var records = [];
12693         for(var i = 0; i < c; i++){
12694                 var n = root[i];
12695             var values = {};
12696             var id = this.getId(n);
12697             for(var j = 0; j < fl; j++){
12698                 f = fi[j];
12699             var v = this.ef[j](n);
12700             if (!f.convert) {
12701                 Roo.log('missing convert for ' + f.name);
12702                 Roo.log(f);
12703                 continue;
12704             }
12705             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12706             }
12707             var record = new Record(values, id);
12708             record.json = n;
12709             records[i] = record;
12710         }
12711         return {
12712             raw : o,
12713             success : success,
12714             records : records,
12715             totalRecords : totalRecords
12716         };
12717     }
12718 });/*
12719  * Based on:
12720  * Ext JS Library 1.1.1
12721  * Copyright(c) 2006-2007, Ext JS, LLC.
12722  *
12723  * Originally Released Under LGPL - original licence link has changed is not relivant.
12724  *
12725  * Fork - LGPL
12726  * <script type="text/javascript">
12727  */
12728
12729 /**
12730  * @class Roo.data.ArrayReader
12731  * @extends Roo.data.DataReader
12732  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12733  * Each element of that Array represents a row of data fields. The
12734  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12735  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12736  * <p>
12737  * Example code:.
12738  * <pre><code>
12739 var RecordDef = Roo.data.Record.create([
12740     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12741     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12742 ]);
12743 var myReader = new Roo.data.ArrayReader({
12744     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12745 }, RecordDef);
12746 </code></pre>
12747  * <p>
12748  * This would consume an Array like this:
12749  * <pre><code>
12750 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12751   </code></pre>
12752  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12753  * @constructor
12754  * Create a new JsonReader
12755  * @param {Object} meta Metadata configuration options.
12756  * @param {Object} recordType Either an Array of field definition objects
12757  * as specified to {@link Roo.data.Record#create},
12758  * or an {@link Roo.data.Record} object
12759  * created using {@link Roo.data.Record#create}.
12760  */
12761 Roo.data.ArrayReader = function(meta, recordType){
12762     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12763 };
12764
12765 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12766     /**
12767      * Create a data block containing Roo.data.Records from an XML document.
12768      * @param {Object} o An Array of row objects which represents the dataset.
12769      * @return {Object} data A data block which is used by an Roo.data.Store object as
12770      * a cache of Roo.data.Records.
12771      */
12772     readRecords : function(o){
12773         var sid = this.meta ? this.meta.id : null;
12774         var recordType = this.recordType, fields = recordType.prototype.fields;
12775         var records = [];
12776         var root = o;
12777             for(var i = 0; i < root.length; i++){
12778                     var n = root[i];
12779                 var values = {};
12780                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12781                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12782                 var f = fields.items[j];
12783                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12784                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12785                 v = f.convert(v);
12786                 values[f.name] = v;
12787             }
12788                 var record = new recordType(values, id);
12789                 record.json = n;
12790                 records[records.length] = record;
12791             }
12792             return {
12793                 records : records,
12794                 totalRecords : records.length
12795             };
12796     }
12797 });/*
12798  * - LGPL
12799  * * 
12800  */
12801
12802 /**
12803  * @class Roo.bootstrap.ComboBox
12804  * @extends Roo.bootstrap.TriggerField
12805  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12806  * @cfg {Boolean} append (true|false) default false
12807  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12808  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12809  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12810  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12811  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12812  * @cfg {Boolean} animate default true
12813  * @cfg {Boolean} emptyResultText only for touch device
12814  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12815  * @cfg {String} emptyTitle default ''
12816  * @constructor
12817  * Create a new ComboBox.
12818  * @param {Object} config Configuration options
12819  */
12820 Roo.bootstrap.ComboBox = function(config){
12821     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12822     this.addEvents({
12823         /**
12824          * @event expand
12825          * Fires when the dropdown list is expanded
12826         * @param {Roo.bootstrap.ComboBox} combo This combo box
12827         */
12828         'expand' : true,
12829         /**
12830          * @event collapse
12831          * Fires when the dropdown list is collapsed
12832         * @param {Roo.bootstrap.ComboBox} combo This combo box
12833         */
12834         'collapse' : true,
12835         /**
12836          * @event beforeselect
12837          * Fires before a list item is selected. Return false to cancel the selection.
12838         * @param {Roo.bootstrap.ComboBox} combo This combo box
12839         * @param {Roo.data.Record} record The data record returned from the underlying store
12840         * @param {Number} index The index of the selected item in the dropdown list
12841         */
12842         'beforeselect' : true,
12843         /**
12844          * @event select
12845          * Fires when a list item is selected
12846         * @param {Roo.bootstrap.ComboBox} combo This combo box
12847         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12848         * @param {Number} index The index of the selected item in the dropdown list
12849         */
12850         'select' : true,
12851         /**
12852          * @event beforequery
12853          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12854          * The event object passed has these properties:
12855         * @param {Roo.bootstrap.ComboBox} combo This combo box
12856         * @param {String} query The query
12857         * @param {Boolean} forceAll true to force "all" query
12858         * @param {Boolean} cancel true to cancel the query
12859         * @param {Object} e The query event object
12860         */
12861         'beforequery': true,
12862          /**
12863          * @event add
12864          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12865         * @param {Roo.bootstrap.ComboBox} combo This combo box
12866         */
12867         'add' : true,
12868         /**
12869          * @event edit
12870          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12871         * @param {Roo.bootstrap.ComboBox} combo This combo box
12872         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12873         */
12874         'edit' : true,
12875         /**
12876          * @event remove
12877          * Fires when the remove value from the combobox array
12878         * @param {Roo.bootstrap.ComboBox} combo This combo box
12879         */
12880         'remove' : true,
12881         /**
12882          * @event afterremove
12883          * Fires when the remove value from the combobox array
12884         * @param {Roo.bootstrap.ComboBox} combo This combo box
12885         */
12886         'afterremove' : true,
12887         /**
12888          * @event specialfilter
12889          * Fires when specialfilter
12890             * @param {Roo.bootstrap.ComboBox} combo This combo box
12891             */
12892         'specialfilter' : true,
12893         /**
12894          * @event tick
12895          * Fires when tick the element
12896             * @param {Roo.bootstrap.ComboBox} combo This combo box
12897             */
12898         'tick' : true,
12899         /**
12900          * @event touchviewdisplay
12901          * Fires when touch view require special display (default is using displayField)
12902             * @param {Roo.bootstrap.ComboBox} combo This combo box
12903             * @param {Object} cfg set html .
12904             */
12905         'touchviewdisplay' : true
12906         
12907     });
12908     
12909     this.item = [];
12910     this.tickItems = [];
12911     
12912     this.selectedIndex = -1;
12913     if(this.mode == 'local'){
12914         if(config.queryDelay === undefined){
12915             this.queryDelay = 10;
12916         }
12917         if(config.minChars === undefined){
12918             this.minChars = 0;
12919         }
12920     }
12921 };
12922
12923 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12924      
12925     /**
12926      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12927      * rendering into an Roo.Editor, defaults to false)
12928      */
12929     /**
12930      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12931      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12932      */
12933     /**
12934      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12935      */
12936     /**
12937      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12938      * the dropdown list (defaults to undefined, with no header element)
12939      */
12940
12941      /**
12942      * @cfg {String/Roo.Template} tpl The template to use to render the output
12943      */
12944      
12945      /**
12946      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12947      */
12948     listWidth: undefined,
12949     /**
12950      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12951      * mode = 'remote' or 'text' if mode = 'local')
12952      */
12953     displayField: undefined,
12954     
12955     /**
12956      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12957      * mode = 'remote' or 'value' if mode = 'local'). 
12958      * Note: use of a valueField requires the user make a selection
12959      * in order for a value to be mapped.
12960      */
12961     valueField: undefined,
12962     /**
12963      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12964      */
12965     modalTitle : '',
12966     
12967     /**
12968      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12969      * field's data value (defaults to the underlying DOM element's name)
12970      */
12971     hiddenName: undefined,
12972     /**
12973      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12974      */
12975     listClass: '',
12976     /**
12977      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12978      */
12979     selectedClass: 'active',
12980     
12981     /**
12982      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12983      */
12984     shadow:'sides',
12985     /**
12986      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12987      * anchor positions (defaults to 'tl-bl')
12988      */
12989     listAlign: 'tl-bl?',
12990     /**
12991      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12992      */
12993     maxHeight: 300,
12994     /**
12995      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12996      * query specified by the allQuery config option (defaults to 'query')
12997      */
12998     triggerAction: 'query',
12999     /**
13000      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13001      * (defaults to 4, does not apply if editable = false)
13002      */
13003     minChars : 4,
13004     /**
13005      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13006      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13007      */
13008     typeAhead: false,
13009     /**
13010      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13011      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13012      */
13013     queryDelay: 500,
13014     /**
13015      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13016      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13017      */
13018     pageSize: 0,
13019     /**
13020      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13021      * when editable = true (defaults to false)
13022      */
13023     selectOnFocus:false,
13024     /**
13025      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13026      */
13027     queryParam: 'query',
13028     /**
13029      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13030      * when mode = 'remote' (defaults to 'Loading...')
13031      */
13032     loadingText: 'Loading...',
13033     /**
13034      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13035      */
13036     resizable: false,
13037     /**
13038      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13039      */
13040     handleHeight : 8,
13041     /**
13042      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13043      * traditional select (defaults to true)
13044      */
13045     editable: true,
13046     /**
13047      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13048      */
13049     allQuery: '',
13050     /**
13051      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13052      */
13053     mode: 'remote',
13054     /**
13055      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13056      * listWidth has a higher value)
13057      */
13058     minListWidth : 70,
13059     /**
13060      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13061      * allow the user to set arbitrary text into the field (defaults to false)
13062      */
13063     forceSelection:false,
13064     /**
13065      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13066      * if typeAhead = true (defaults to 250)
13067      */
13068     typeAheadDelay : 250,
13069     /**
13070      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13071      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13072      */
13073     valueNotFoundText : undefined,
13074     /**
13075      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13076      */
13077     blockFocus : false,
13078     
13079     /**
13080      * @cfg {Boolean} disableClear Disable showing of clear button.
13081      */
13082     disableClear : false,
13083     /**
13084      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13085      */
13086     alwaysQuery : false,
13087     
13088     /**
13089      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13090      */
13091     multiple : false,
13092     
13093     /**
13094      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13095      */
13096     invalidClass : "has-warning",
13097     
13098     /**
13099      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13100      */
13101     validClass : "has-success",
13102     
13103     /**
13104      * @cfg {Boolean} specialFilter (true|false) special filter default false
13105      */
13106     specialFilter : false,
13107     
13108     /**
13109      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13110      */
13111     mobileTouchView : true,
13112     
13113     /**
13114      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13115      */
13116     useNativeIOS : false,
13117     
13118     /**
13119      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13120      */
13121     mobile_restrict_height : false,
13122     
13123     ios_options : false,
13124     
13125     //private
13126     addicon : false,
13127     editicon: false,
13128     
13129     page: 0,
13130     hasQuery: false,
13131     append: false,
13132     loadNext: false,
13133     autoFocus : true,
13134     tickable : false,
13135     btnPosition : 'right',
13136     triggerList : true,
13137     showToggleBtn : true,
13138     animate : true,
13139     emptyResultText: 'Empty',
13140     triggerText : 'Select',
13141     emptyTitle : '',
13142     
13143     // element that contains real text value.. (when hidden is used..)
13144     
13145     getAutoCreate : function()
13146     {   
13147         var cfg = false;
13148         //render
13149         /*
13150          * Render classic select for iso
13151          */
13152         
13153         if(Roo.isIOS && this.useNativeIOS){
13154             cfg = this.getAutoCreateNativeIOS();
13155             return cfg;
13156         }
13157         
13158         /*
13159          * Touch Devices
13160          */
13161         
13162         if(Roo.isTouch && this.mobileTouchView){
13163             cfg = this.getAutoCreateTouchView();
13164             return cfg;;
13165         }
13166         
13167         /*
13168          *  Normal ComboBox
13169          */
13170         if(!this.tickable){
13171             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13172             return cfg;
13173         }
13174         
13175         /*
13176          *  ComboBox with tickable selections
13177          */
13178              
13179         var align = this.labelAlign || this.parentLabelAlign();
13180         
13181         cfg = {
13182             cls : 'form-group roo-combobox-tickable' //input-group
13183         };
13184         
13185         var btn_text_select = '';
13186         var btn_text_done = '';
13187         var btn_text_cancel = '';
13188         
13189         if (this.btn_text_show) {
13190             btn_text_select = 'Select';
13191             btn_text_done = 'Done';
13192             btn_text_cancel = 'Cancel'; 
13193         }
13194         
13195         var buttons = {
13196             tag : 'div',
13197             cls : 'tickable-buttons',
13198             cn : [
13199                 {
13200                     tag : 'button',
13201                     type : 'button',
13202                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13203                     //html : this.triggerText
13204                     html: btn_text_select
13205                 },
13206                 {
13207                     tag : 'button',
13208                     type : 'button',
13209                     name : 'ok',
13210                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13211                     //html : 'Done'
13212                     html: btn_text_done
13213                 },
13214                 {
13215                     tag : 'button',
13216                     type : 'button',
13217                     name : 'cancel',
13218                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13219                     //html : 'Cancel'
13220                     html: btn_text_cancel
13221                 }
13222             ]
13223         };
13224         
13225         if(this.editable){
13226             buttons.cn.unshift({
13227                 tag: 'input',
13228                 cls: 'roo-select2-search-field-input'
13229             });
13230         }
13231         
13232         var _this = this;
13233         
13234         Roo.each(buttons.cn, function(c){
13235             if (_this.size) {
13236                 c.cls += ' btn-' + _this.size;
13237             }
13238
13239             if (_this.disabled) {
13240                 c.disabled = true;
13241             }
13242         });
13243         
13244         var box = {
13245             tag: 'div',
13246             cn: [
13247                 {
13248                     tag: 'input',
13249                     type : 'hidden',
13250                     cls: 'form-hidden-field'
13251                 },
13252                 {
13253                     tag: 'ul',
13254                     cls: 'roo-select2-choices',
13255                     cn:[
13256                         {
13257                             tag: 'li',
13258                             cls: 'roo-select2-search-field',
13259                             cn: [
13260                                 buttons
13261                             ]
13262                         }
13263                     ]
13264                 }
13265             ]
13266         };
13267         
13268         var combobox = {
13269             cls: 'roo-select2-container input-group roo-select2-container-multi',
13270             cn: [
13271                 box
13272 //                {
13273 //                    tag: 'ul',
13274 //                    cls: 'typeahead typeahead-long dropdown-menu',
13275 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13276 //                }
13277             ]
13278         };
13279         
13280         if(this.hasFeedback && !this.allowBlank){
13281             
13282             var feedback = {
13283                 tag: 'span',
13284                 cls: 'glyphicon form-control-feedback'
13285             };
13286
13287             combobox.cn.push(feedback);
13288         }
13289         
13290         
13291         if (align ==='left' && this.fieldLabel.length) {
13292             
13293             cfg.cls += ' roo-form-group-label-left';
13294             
13295             cfg.cn = [
13296                 {
13297                     tag : 'i',
13298                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13299                     tooltip : 'This field is required'
13300                 },
13301                 {
13302                     tag: 'label',
13303                     'for' :  id,
13304                     cls : 'control-label',
13305                     html : this.fieldLabel
13306
13307                 },
13308                 {
13309                     cls : "", 
13310                     cn: [
13311                         combobox
13312                     ]
13313                 }
13314
13315             ];
13316             
13317             var labelCfg = cfg.cn[1];
13318             var contentCfg = cfg.cn[2];
13319             
13320
13321             if(this.indicatorpos == 'right'){
13322                 
13323                 cfg.cn = [
13324                     {
13325                         tag: 'label',
13326                         'for' :  id,
13327                         cls : 'control-label',
13328                         cn : [
13329                             {
13330                                 tag : 'span',
13331                                 html : this.fieldLabel
13332                             },
13333                             {
13334                                 tag : 'i',
13335                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13336                                 tooltip : 'This field is required'
13337                             }
13338                         ]
13339                     },
13340                     {
13341                         cls : "",
13342                         cn: [
13343                             combobox
13344                         ]
13345                     }
13346
13347                 ];
13348                 
13349                 
13350                 
13351                 labelCfg = cfg.cn[0];
13352                 contentCfg = cfg.cn[1];
13353             
13354             }
13355             
13356             if(this.labelWidth > 12){
13357                 labelCfg.style = "width: " + this.labelWidth + 'px';
13358             }
13359             
13360             if(this.labelWidth < 13 && this.labelmd == 0){
13361                 this.labelmd = this.labelWidth;
13362             }
13363             
13364             if(this.labellg > 0){
13365                 labelCfg.cls += ' col-lg-' + this.labellg;
13366                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13367             }
13368             
13369             if(this.labelmd > 0){
13370                 labelCfg.cls += ' col-md-' + this.labelmd;
13371                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13372             }
13373             
13374             if(this.labelsm > 0){
13375                 labelCfg.cls += ' col-sm-' + this.labelsm;
13376                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13377             }
13378             
13379             if(this.labelxs > 0){
13380                 labelCfg.cls += ' col-xs-' + this.labelxs;
13381                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13382             }
13383                 
13384                 
13385         } else if ( this.fieldLabel.length) {
13386 //                Roo.log(" label");
13387                  cfg.cn = [
13388                     {
13389                         tag : 'i',
13390                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13391                         tooltip : 'This field is required'
13392                     },
13393                     {
13394                         tag: 'label',
13395                         //cls : 'input-group-addon',
13396                         html : this.fieldLabel
13397                     },
13398                     combobox
13399                 ];
13400                 
13401                 if(this.indicatorpos == 'right'){
13402                     cfg.cn = [
13403                         {
13404                             tag: 'label',
13405                             //cls : 'input-group-addon',
13406                             html : this.fieldLabel
13407                         },
13408                         {
13409                             tag : 'i',
13410                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13411                             tooltip : 'This field is required'
13412                         },
13413                         combobox
13414                     ];
13415                     
13416                 }
13417
13418         } else {
13419             
13420 //                Roo.log(" no label && no align");
13421                 cfg = combobox
13422                      
13423                 
13424         }
13425          
13426         var settings=this;
13427         ['xs','sm','md','lg'].map(function(size){
13428             if (settings[size]) {
13429                 cfg.cls += ' col-' + size + '-' + settings[size];
13430             }
13431         });
13432         
13433         return cfg;
13434         
13435     },
13436     
13437     _initEventsCalled : false,
13438     
13439     // private
13440     initEvents: function()
13441     {   
13442         if (this._initEventsCalled) { // as we call render... prevent looping...
13443             return;
13444         }
13445         this._initEventsCalled = true;
13446         
13447         if (!this.store) {
13448             throw "can not find store for combo";
13449         }
13450         
13451         this.indicator = this.indicatorEl();
13452         
13453         this.store = Roo.factory(this.store, Roo.data);
13454         this.store.parent = this;
13455         
13456         // if we are building from html. then this element is so complex, that we can not really
13457         // use the rendered HTML.
13458         // so we have to trash and replace the previous code.
13459         if (Roo.XComponent.build_from_html) {
13460             // remove this element....
13461             var e = this.el.dom, k=0;
13462             while (e ) { e = e.previousSibling;  ++k;}
13463
13464             this.el.remove();
13465             
13466             this.el=false;
13467             this.rendered = false;
13468             
13469             this.render(this.parent().getChildContainer(true), k);
13470         }
13471         
13472         if(Roo.isIOS && this.useNativeIOS){
13473             this.initIOSView();
13474             return;
13475         }
13476         
13477         /*
13478          * Touch Devices
13479          */
13480         
13481         if(Roo.isTouch && this.mobileTouchView){
13482             this.initTouchView();
13483             return;
13484         }
13485         
13486         if(this.tickable){
13487             this.initTickableEvents();
13488             return;
13489         }
13490         
13491         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13492         
13493         if(this.hiddenName){
13494             
13495             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13496             
13497             this.hiddenField.dom.value =
13498                 this.hiddenValue !== undefined ? this.hiddenValue :
13499                 this.value !== undefined ? this.value : '';
13500
13501             // prevent input submission
13502             this.el.dom.removeAttribute('name');
13503             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13504              
13505              
13506         }
13507         //if(Roo.isGecko){
13508         //    this.el.dom.setAttribute('autocomplete', 'off');
13509         //}
13510         
13511         var cls = 'x-combo-list';
13512         
13513         //this.list = new Roo.Layer({
13514         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13515         //});
13516         
13517         var _this = this;
13518         
13519         (function(){
13520             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13521             _this.list.setWidth(lw);
13522         }).defer(100);
13523         
13524         this.list.on('mouseover', this.onViewOver, this);
13525         this.list.on('mousemove', this.onViewMove, this);
13526         this.list.on('scroll', this.onViewScroll, this);
13527         
13528         /*
13529         this.list.swallowEvent('mousewheel');
13530         this.assetHeight = 0;
13531
13532         if(this.title){
13533             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13534             this.assetHeight += this.header.getHeight();
13535         }
13536
13537         this.innerList = this.list.createChild({cls:cls+'-inner'});
13538         this.innerList.on('mouseover', this.onViewOver, this);
13539         this.innerList.on('mousemove', this.onViewMove, this);
13540         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13541         
13542         if(this.allowBlank && !this.pageSize && !this.disableClear){
13543             this.footer = this.list.createChild({cls:cls+'-ft'});
13544             this.pageTb = new Roo.Toolbar(this.footer);
13545            
13546         }
13547         if(this.pageSize){
13548             this.footer = this.list.createChild({cls:cls+'-ft'});
13549             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13550                     {pageSize: this.pageSize});
13551             
13552         }
13553         
13554         if (this.pageTb && this.allowBlank && !this.disableClear) {
13555             var _this = this;
13556             this.pageTb.add(new Roo.Toolbar.Fill(), {
13557                 cls: 'x-btn-icon x-btn-clear',
13558                 text: '&#160;',
13559                 handler: function()
13560                 {
13561                     _this.collapse();
13562                     _this.clearValue();
13563                     _this.onSelect(false, -1);
13564                 }
13565             });
13566         }
13567         if (this.footer) {
13568             this.assetHeight += this.footer.getHeight();
13569         }
13570         */
13571             
13572         if(!this.tpl){
13573             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13574         }
13575
13576         this.view = new Roo.View(this.list, this.tpl, {
13577             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13578         });
13579         //this.view.wrapEl.setDisplayed(false);
13580         this.view.on('click', this.onViewClick, this);
13581         
13582         
13583         this.store.on('beforeload', this.onBeforeLoad, this);
13584         this.store.on('load', this.onLoad, this);
13585         this.store.on('loadexception', this.onLoadException, this);
13586         /*
13587         if(this.resizable){
13588             this.resizer = new Roo.Resizable(this.list,  {
13589                pinned:true, handles:'se'
13590             });
13591             this.resizer.on('resize', function(r, w, h){
13592                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13593                 this.listWidth = w;
13594                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13595                 this.restrictHeight();
13596             }, this);
13597             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13598         }
13599         */
13600         if(!this.editable){
13601             this.editable = true;
13602             this.setEditable(false);
13603         }
13604         
13605         /*
13606         
13607         if (typeof(this.events.add.listeners) != 'undefined') {
13608             
13609             this.addicon = this.wrap.createChild(
13610                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13611        
13612             this.addicon.on('click', function(e) {
13613                 this.fireEvent('add', this);
13614             }, this);
13615         }
13616         if (typeof(this.events.edit.listeners) != 'undefined') {
13617             
13618             this.editicon = this.wrap.createChild(
13619                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13620             if (this.addicon) {
13621                 this.editicon.setStyle('margin-left', '40px');
13622             }
13623             this.editicon.on('click', function(e) {
13624                 
13625                 // we fire even  if inothing is selected..
13626                 this.fireEvent('edit', this, this.lastData );
13627                 
13628             }, this);
13629         }
13630         */
13631         
13632         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13633             "up" : function(e){
13634                 this.inKeyMode = true;
13635                 this.selectPrev();
13636             },
13637
13638             "down" : function(e){
13639                 if(!this.isExpanded()){
13640                     this.onTriggerClick();
13641                 }else{
13642                     this.inKeyMode = true;
13643                     this.selectNext();
13644                 }
13645             },
13646
13647             "enter" : function(e){
13648 //                this.onViewClick();
13649                 //return true;
13650                 this.collapse();
13651                 
13652                 if(this.fireEvent("specialkey", this, e)){
13653                     this.onViewClick(false);
13654                 }
13655                 
13656                 return true;
13657             },
13658
13659             "esc" : function(e){
13660                 this.collapse();
13661             },
13662
13663             "tab" : function(e){
13664                 this.collapse();
13665                 
13666                 if(this.fireEvent("specialkey", this, e)){
13667                     this.onViewClick(false);
13668                 }
13669                 
13670                 return true;
13671             },
13672
13673             scope : this,
13674
13675             doRelay : function(foo, bar, hname){
13676                 if(hname == 'down' || this.scope.isExpanded()){
13677                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13678                 }
13679                 return true;
13680             },
13681
13682             forceKeyDown: true
13683         });
13684         
13685         
13686         this.queryDelay = Math.max(this.queryDelay || 10,
13687                 this.mode == 'local' ? 10 : 250);
13688         
13689         
13690         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13691         
13692         if(this.typeAhead){
13693             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13694         }
13695         if(this.editable !== false){
13696             this.inputEl().on("keyup", this.onKeyUp, this);
13697         }
13698         if(this.forceSelection){
13699             this.inputEl().on('blur', this.doForce, this);
13700         }
13701         
13702         if(this.multiple){
13703             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13704             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13705         }
13706     },
13707     
13708     initTickableEvents: function()
13709     {   
13710         this.createList();
13711         
13712         if(this.hiddenName){
13713             
13714             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13715             
13716             this.hiddenField.dom.value =
13717                 this.hiddenValue !== undefined ? this.hiddenValue :
13718                 this.value !== undefined ? this.value : '';
13719
13720             // prevent input submission
13721             this.el.dom.removeAttribute('name');
13722             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13723              
13724              
13725         }
13726         
13727 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13728         
13729         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13730         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13731         if(this.triggerList){
13732             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13733         }
13734          
13735         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13736         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13737         
13738         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13739         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13740         
13741         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13742         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13743         
13744         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13745         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13746         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13747         
13748         this.okBtn.hide();
13749         this.cancelBtn.hide();
13750         
13751         var _this = this;
13752         
13753         (function(){
13754             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13755             _this.list.setWidth(lw);
13756         }).defer(100);
13757         
13758         this.list.on('mouseover', this.onViewOver, this);
13759         this.list.on('mousemove', this.onViewMove, this);
13760         
13761         this.list.on('scroll', this.onViewScroll, this);
13762         
13763         if(!this.tpl){
13764             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13765                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13766         }
13767
13768         this.view = new Roo.View(this.list, this.tpl, {
13769             singleSelect:true,
13770             tickable:true,
13771             parent:this,
13772             store: this.store,
13773             selectedClass: this.selectedClass
13774         });
13775         
13776         //this.view.wrapEl.setDisplayed(false);
13777         this.view.on('click', this.onViewClick, this);
13778         
13779         
13780         
13781         this.store.on('beforeload', this.onBeforeLoad, this);
13782         this.store.on('load', this.onLoad, this);
13783         this.store.on('loadexception', this.onLoadException, this);
13784         
13785         if(this.editable){
13786             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13787                 "up" : function(e){
13788                     this.inKeyMode = true;
13789                     this.selectPrev();
13790                 },
13791
13792                 "down" : function(e){
13793                     this.inKeyMode = true;
13794                     this.selectNext();
13795                 },
13796
13797                 "enter" : function(e){
13798                     if(this.fireEvent("specialkey", this, e)){
13799                         this.onViewClick(false);
13800                     }
13801                     
13802                     return true;
13803                 },
13804
13805                 "esc" : function(e){
13806                     this.onTickableFooterButtonClick(e, false, false);
13807                 },
13808
13809                 "tab" : function(e){
13810                     this.fireEvent("specialkey", this, e);
13811                     
13812                     this.onTickableFooterButtonClick(e, false, false);
13813                     
13814                     return true;
13815                 },
13816
13817                 scope : this,
13818
13819                 doRelay : function(e, fn, key){
13820                     if(this.scope.isExpanded()){
13821                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13822                     }
13823                     return true;
13824                 },
13825
13826                 forceKeyDown: true
13827             });
13828         }
13829         
13830         this.queryDelay = Math.max(this.queryDelay || 10,
13831                 this.mode == 'local' ? 10 : 250);
13832         
13833         
13834         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13835         
13836         if(this.typeAhead){
13837             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13838         }
13839         
13840         if(this.editable !== false){
13841             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13842         }
13843         
13844         this.indicator = this.indicatorEl();
13845         
13846         if(this.indicator){
13847             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13848             this.indicator.hide();
13849         }
13850         
13851     },
13852
13853     onDestroy : function(){
13854         if(this.view){
13855             this.view.setStore(null);
13856             this.view.el.removeAllListeners();
13857             this.view.el.remove();
13858             this.view.purgeListeners();
13859         }
13860         if(this.list){
13861             this.list.dom.innerHTML  = '';
13862         }
13863         
13864         if(this.store){
13865             this.store.un('beforeload', this.onBeforeLoad, this);
13866             this.store.un('load', this.onLoad, this);
13867             this.store.un('loadexception', this.onLoadException, this);
13868         }
13869         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13870     },
13871
13872     // private
13873     fireKey : function(e){
13874         if(e.isNavKeyPress() && !this.list.isVisible()){
13875             this.fireEvent("specialkey", this, e);
13876         }
13877     },
13878
13879     // private
13880     onResize: function(w, h){
13881 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13882 //        
13883 //        if(typeof w != 'number'){
13884 //            // we do not handle it!?!?
13885 //            return;
13886 //        }
13887 //        var tw = this.trigger.getWidth();
13888 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13889 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13890 //        var x = w - tw;
13891 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13892 //            
13893 //        //this.trigger.setStyle('left', x+'px');
13894 //        
13895 //        if(this.list && this.listWidth === undefined){
13896 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13897 //            this.list.setWidth(lw);
13898 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13899 //        }
13900         
13901     
13902         
13903     },
13904
13905     /**
13906      * Allow or prevent the user from directly editing the field text.  If false is passed,
13907      * the user will only be able to select from the items defined in the dropdown list.  This method
13908      * is the runtime equivalent of setting the 'editable' config option at config time.
13909      * @param {Boolean} value True to allow the user to directly edit the field text
13910      */
13911     setEditable : function(value){
13912         if(value == this.editable){
13913             return;
13914         }
13915         this.editable = value;
13916         if(!value){
13917             this.inputEl().dom.setAttribute('readOnly', true);
13918             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13919             this.inputEl().addClass('x-combo-noedit');
13920         }else{
13921             this.inputEl().dom.setAttribute('readOnly', false);
13922             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13923             this.inputEl().removeClass('x-combo-noedit');
13924         }
13925     },
13926
13927     // private
13928     
13929     onBeforeLoad : function(combo,opts){
13930         if(!this.hasFocus){
13931             return;
13932         }
13933          if (!opts.add) {
13934             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13935          }
13936         this.restrictHeight();
13937         this.selectedIndex = -1;
13938     },
13939
13940     // private
13941     onLoad : function(){
13942         
13943         this.hasQuery = false;
13944         
13945         if(!this.hasFocus){
13946             return;
13947         }
13948         
13949         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13950             this.loading.hide();
13951         }
13952         
13953         if(this.store.getCount() > 0){
13954             
13955             this.expand();
13956             this.restrictHeight();
13957             if(this.lastQuery == this.allQuery){
13958                 if(this.editable && !this.tickable){
13959                     this.inputEl().dom.select();
13960                 }
13961                 
13962                 if(
13963                     !this.selectByValue(this.value, true) &&
13964                     this.autoFocus && 
13965                     (
13966                         !this.store.lastOptions ||
13967                         typeof(this.store.lastOptions.add) == 'undefined' || 
13968                         this.store.lastOptions.add != true
13969                     )
13970                 ){
13971                     this.select(0, true);
13972                 }
13973             }else{
13974                 if(this.autoFocus){
13975                     this.selectNext();
13976                 }
13977                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13978                     this.taTask.delay(this.typeAheadDelay);
13979                 }
13980             }
13981         }else{
13982             this.onEmptyResults();
13983         }
13984         
13985         //this.el.focus();
13986     },
13987     // private
13988     onLoadException : function()
13989     {
13990         this.hasQuery = false;
13991         
13992         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13993             this.loading.hide();
13994         }
13995         
13996         if(this.tickable && this.editable){
13997             return;
13998         }
13999         
14000         this.collapse();
14001         // only causes errors at present
14002         //Roo.log(this.store.reader.jsonData);
14003         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14004             // fixme
14005             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14006         //}
14007         
14008         
14009     },
14010     // private
14011     onTypeAhead : function(){
14012         if(this.store.getCount() > 0){
14013             var r = this.store.getAt(0);
14014             var newValue = r.data[this.displayField];
14015             var len = newValue.length;
14016             var selStart = this.getRawValue().length;
14017             
14018             if(selStart != len){
14019                 this.setRawValue(newValue);
14020                 this.selectText(selStart, newValue.length);
14021             }
14022         }
14023     },
14024
14025     // private
14026     onSelect : function(record, index){
14027         
14028         if(this.fireEvent('beforeselect', this, record, index) !== false){
14029         
14030             this.setFromData(index > -1 ? record.data : false);
14031             
14032             this.collapse();
14033             this.fireEvent('select', this, record, index);
14034         }
14035     },
14036
14037     /**
14038      * Returns the currently selected field value or empty string if no value is set.
14039      * @return {String} value The selected value
14040      */
14041     getValue : function()
14042     {
14043         if(Roo.isIOS && this.useNativeIOS){
14044             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14045         }
14046         
14047         if(this.multiple){
14048             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14049         }
14050         
14051         if(this.valueField){
14052             return typeof this.value != 'undefined' ? this.value : '';
14053         }else{
14054             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14055         }
14056     },
14057     
14058     getRawValue : function()
14059     {
14060         if(Roo.isIOS && this.useNativeIOS){
14061             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14062         }
14063         
14064         var v = this.inputEl().getValue();
14065         
14066         return v;
14067     },
14068
14069     /**
14070      * Clears any text/value currently set in the field
14071      */
14072     clearValue : function(){
14073         
14074         if(this.hiddenField){
14075             this.hiddenField.dom.value = '';
14076         }
14077         this.value = '';
14078         this.setRawValue('');
14079         this.lastSelectionText = '';
14080         this.lastData = false;
14081         
14082         var close = this.closeTriggerEl();
14083         
14084         if(close){
14085             close.hide();
14086         }
14087         
14088         this.validate();
14089         
14090     },
14091
14092     /**
14093      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14094      * will be displayed in the field.  If the value does not match the data value of an existing item,
14095      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14096      * Otherwise the field will be blank (although the value will still be set).
14097      * @param {String} value The value to match
14098      */
14099     setValue : function(v)
14100     {
14101         if(Roo.isIOS && this.useNativeIOS){
14102             this.setIOSValue(v);
14103             return;
14104         }
14105         
14106         if(this.multiple){
14107             this.syncValue();
14108             return;
14109         }
14110         
14111         var text = v;
14112         if(this.valueField){
14113             var r = this.findRecord(this.valueField, v);
14114             if(r){
14115                 text = r.data[this.displayField];
14116             }else if(this.valueNotFoundText !== undefined){
14117                 text = this.valueNotFoundText;
14118             }
14119         }
14120         this.lastSelectionText = text;
14121         if(this.hiddenField){
14122             this.hiddenField.dom.value = v;
14123         }
14124         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14125         this.value = v;
14126         
14127         var close = this.closeTriggerEl();
14128         
14129         if(close){
14130             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14131         }
14132         
14133         this.validate();
14134     },
14135     /**
14136      * @property {Object} the last set data for the element
14137      */
14138     
14139     lastData : false,
14140     /**
14141      * Sets the value of the field based on a object which is related to the record format for the store.
14142      * @param {Object} value the value to set as. or false on reset?
14143      */
14144     setFromData : function(o){
14145         
14146         if(this.multiple){
14147             this.addItem(o);
14148             return;
14149         }
14150             
14151         var dv = ''; // display value
14152         var vv = ''; // value value..
14153         this.lastData = o;
14154         if (this.displayField) {
14155             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14156         } else {
14157             // this is an error condition!!!
14158             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14159         }
14160         
14161         if(this.valueField){
14162             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14163         }
14164         
14165         var close = this.closeTriggerEl();
14166         
14167         if(close){
14168             if(dv.length || vv * 1 > 0){
14169                 close.show() ;
14170                 this.blockFocus=true;
14171             } else {
14172                 close.hide();
14173             }             
14174         }
14175         
14176         if(this.hiddenField){
14177             this.hiddenField.dom.value = vv;
14178             
14179             this.lastSelectionText = dv;
14180             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14181             this.value = vv;
14182             return;
14183         }
14184         // no hidden field.. - we store the value in 'value', but still display
14185         // display field!!!!
14186         this.lastSelectionText = dv;
14187         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14188         this.value = vv;
14189         
14190         
14191         
14192     },
14193     // private
14194     reset : function(){
14195         // overridden so that last data is reset..
14196         
14197         if(this.multiple){
14198             this.clearItem();
14199             return;
14200         }
14201         
14202         this.setValue(this.originalValue);
14203         //this.clearInvalid();
14204         this.lastData = false;
14205         if (this.view) {
14206             this.view.clearSelections();
14207         }
14208         
14209         this.validate();
14210     },
14211     // private
14212     findRecord : function(prop, value){
14213         var record;
14214         if(this.store.getCount() > 0){
14215             this.store.each(function(r){
14216                 if(r.data[prop] == value){
14217                     record = r;
14218                     return false;
14219                 }
14220                 return true;
14221             });
14222         }
14223         return record;
14224     },
14225     
14226     getName: function()
14227     {
14228         // returns hidden if it's set..
14229         if (!this.rendered) {return ''};
14230         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14231         
14232     },
14233     // private
14234     onViewMove : function(e, t){
14235         this.inKeyMode = false;
14236     },
14237
14238     // private
14239     onViewOver : function(e, t){
14240         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14241             return;
14242         }
14243         var item = this.view.findItemFromChild(t);
14244         
14245         if(item){
14246             var index = this.view.indexOf(item);
14247             this.select(index, false);
14248         }
14249     },
14250
14251     // private
14252     onViewClick : function(view, doFocus, el, e)
14253     {
14254         var index = this.view.getSelectedIndexes()[0];
14255         
14256         var r = this.store.getAt(index);
14257         
14258         if(this.tickable){
14259             
14260             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14261                 return;
14262             }
14263             
14264             var rm = false;
14265             var _this = this;
14266             
14267             Roo.each(this.tickItems, function(v,k){
14268                 
14269                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14270                     Roo.log(v);
14271                     _this.tickItems.splice(k, 1);
14272                     
14273                     if(typeof(e) == 'undefined' && view == false){
14274                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14275                     }
14276                     
14277                     rm = true;
14278                     return;
14279                 }
14280             });
14281             
14282             if(rm){
14283                 return;
14284             }
14285             
14286             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14287                 this.tickItems.push(r.data);
14288             }
14289             
14290             if(typeof(e) == 'undefined' && view == false){
14291                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14292             }
14293                     
14294             return;
14295         }
14296         
14297         if(r){
14298             this.onSelect(r, index);
14299         }
14300         if(doFocus !== false && !this.blockFocus){
14301             this.inputEl().focus();
14302         }
14303     },
14304
14305     // private
14306     restrictHeight : function(){
14307         //this.innerList.dom.style.height = '';
14308         //var inner = this.innerList.dom;
14309         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14310         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14311         //this.list.beginUpdate();
14312         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14313         this.list.alignTo(this.inputEl(), this.listAlign);
14314         this.list.alignTo(this.inputEl(), this.listAlign);
14315         //this.list.endUpdate();
14316     },
14317
14318     // private
14319     onEmptyResults : function(){
14320         
14321         if(this.tickable && this.editable){
14322             this.hasFocus = false;
14323             this.restrictHeight();
14324             return;
14325         }
14326         
14327         this.collapse();
14328     },
14329
14330     /**
14331      * Returns true if the dropdown list is expanded, else false.
14332      */
14333     isExpanded : function(){
14334         return this.list.isVisible();
14335     },
14336
14337     /**
14338      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14339      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14340      * @param {String} value The data value of the item to select
14341      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14342      * selected item if it is not currently in view (defaults to true)
14343      * @return {Boolean} True if the value matched an item in the list, else false
14344      */
14345     selectByValue : function(v, scrollIntoView){
14346         if(v !== undefined && v !== null){
14347             var r = this.findRecord(this.valueField || this.displayField, v);
14348             if(r){
14349                 this.select(this.store.indexOf(r), scrollIntoView);
14350                 return true;
14351             }
14352         }
14353         return false;
14354     },
14355
14356     /**
14357      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14358      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14359      * @param {Number} index The zero-based index of the list item to select
14360      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14361      * selected item if it is not currently in view (defaults to true)
14362      */
14363     select : function(index, scrollIntoView){
14364         this.selectedIndex = index;
14365         this.view.select(index);
14366         if(scrollIntoView !== false){
14367             var el = this.view.getNode(index);
14368             /*
14369              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14370              */
14371             if(el){
14372                 this.list.scrollChildIntoView(el, false);
14373             }
14374         }
14375     },
14376
14377     // private
14378     selectNext : function(){
14379         var ct = this.store.getCount();
14380         if(ct > 0){
14381             if(this.selectedIndex == -1){
14382                 this.select(0);
14383             }else if(this.selectedIndex < ct-1){
14384                 this.select(this.selectedIndex+1);
14385             }
14386         }
14387     },
14388
14389     // private
14390     selectPrev : function(){
14391         var ct = this.store.getCount();
14392         if(ct > 0){
14393             if(this.selectedIndex == -1){
14394                 this.select(0);
14395             }else if(this.selectedIndex != 0){
14396                 this.select(this.selectedIndex-1);
14397             }
14398         }
14399     },
14400
14401     // private
14402     onKeyUp : function(e){
14403         if(this.editable !== false && !e.isSpecialKey()){
14404             this.lastKey = e.getKey();
14405             this.dqTask.delay(this.queryDelay);
14406         }
14407     },
14408
14409     // private
14410     validateBlur : function(){
14411         return !this.list || !this.list.isVisible();   
14412     },
14413
14414     // private
14415     initQuery : function(){
14416         
14417         var v = this.getRawValue();
14418         
14419         if(this.tickable && this.editable){
14420             v = this.tickableInputEl().getValue();
14421         }
14422         
14423         this.doQuery(v);
14424     },
14425
14426     // private
14427     doForce : function(){
14428         if(this.inputEl().dom.value.length > 0){
14429             this.inputEl().dom.value =
14430                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14431              
14432         }
14433     },
14434
14435     /**
14436      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14437      * query allowing the query action to be canceled if needed.
14438      * @param {String} query The SQL query to execute
14439      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14440      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14441      * saved in the current store (defaults to false)
14442      */
14443     doQuery : function(q, forceAll){
14444         
14445         if(q === undefined || q === null){
14446             q = '';
14447         }
14448         var qe = {
14449             query: q,
14450             forceAll: forceAll,
14451             combo: this,
14452             cancel:false
14453         };
14454         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14455             return false;
14456         }
14457         q = qe.query;
14458         
14459         forceAll = qe.forceAll;
14460         if(forceAll === true || (q.length >= this.minChars)){
14461             
14462             this.hasQuery = true;
14463             
14464             if(this.lastQuery != q || this.alwaysQuery){
14465                 this.lastQuery = q;
14466                 if(this.mode == 'local'){
14467                     this.selectedIndex = -1;
14468                     if(forceAll){
14469                         this.store.clearFilter();
14470                     }else{
14471                         
14472                         if(this.specialFilter){
14473                             this.fireEvent('specialfilter', this);
14474                             this.onLoad();
14475                             return;
14476                         }
14477                         
14478                         this.store.filter(this.displayField, q);
14479                     }
14480                     
14481                     this.store.fireEvent("datachanged", this.store);
14482                     
14483                     this.onLoad();
14484                     
14485                     
14486                 }else{
14487                     
14488                     this.store.baseParams[this.queryParam] = q;
14489                     
14490                     var options = {params : this.getParams(q)};
14491                     
14492                     if(this.loadNext){
14493                         options.add = true;
14494                         options.params.start = this.page * this.pageSize;
14495                     }
14496                     
14497                     this.store.load(options);
14498                     
14499                     /*
14500                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14501                      *  we should expand the list on onLoad
14502                      *  so command out it
14503                      */
14504 //                    this.expand();
14505                 }
14506             }else{
14507                 this.selectedIndex = -1;
14508                 this.onLoad();   
14509             }
14510         }
14511         
14512         this.loadNext = false;
14513     },
14514     
14515     // private
14516     getParams : function(q){
14517         var p = {};
14518         //p[this.queryParam] = q;
14519         
14520         if(this.pageSize){
14521             p.start = 0;
14522             p.limit = this.pageSize;
14523         }
14524         return p;
14525     },
14526
14527     /**
14528      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14529      */
14530     collapse : function(){
14531         if(!this.isExpanded()){
14532             return;
14533         }
14534         
14535         this.list.hide();
14536         
14537         this.hasFocus = false;
14538         
14539         if(this.tickable){
14540             this.okBtn.hide();
14541             this.cancelBtn.hide();
14542             this.trigger.show();
14543             
14544             if(this.editable){
14545                 this.tickableInputEl().dom.value = '';
14546                 this.tickableInputEl().blur();
14547             }
14548             
14549         }
14550         
14551         Roo.get(document).un('mousedown', this.collapseIf, this);
14552         Roo.get(document).un('mousewheel', this.collapseIf, this);
14553         if (!this.editable) {
14554             Roo.get(document).un('keydown', this.listKeyPress, this);
14555         }
14556         this.fireEvent('collapse', this);
14557         
14558         this.validate();
14559     },
14560
14561     // private
14562     collapseIf : function(e){
14563         var in_combo  = e.within(this.el);
14564         var in_list =  e.within(this.list);
14565         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14566         
14567         if (in_combo || in_list || is_list) {
14568             //e.stopPropagation();
14569             return;
14570         }
14571         
14572         if(this.tickable){
14573             this.onTickableFooterButtonClick(e, false, false);
14574         }
14575
14576         this.collapse();
14577         
14578     },
14579
14580     /**
14581      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14582      */
14583     expand : function(){
14584        
14585         if(this.isExpanded() || !this.hasFocus){
14586             return;
14587         }
14588         
14589         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14590         this.list.setWidth(lw);
14591         
14592         Roo.log('expand');
14593         
14594         this.list.show();
14595         
14596         this.restrictHeight();
14597         
14598         if(this.tickable){
14599             
14600             this.tickItems = Roo.apply([], this.item);
14601             
14602             this.okBtn.show();
14603             this.cancelBtn.show();
14604             this.trigger.hide();
14605             
14606             if(this.editable){
14607                 this.tickableInputEl().focus();
14608             }
14609             
14610         }
14611         
14612         Roo.get(document).on('mousedown', this.collapseIf, this);
14613         Roo.get(document).on('mousewheel', this.collapseIf, this);
14614         if (!this.editable) {
14615             Roo.get(document).on('keydown', this.listKeyPress, this);
14616         }
14617         
14618         this.fireEvent('expand', this);
14619     },
14620
14621     // private
14622     // Implements the default empty TriggerField.onTriggerClick function
14623     onTriggerClick : function(e)
14624     {
14625         Roo.log('trigger click');
14626         
14627         if(this.disabled || !this.triggerList){
14628             return;
14629         }
14630         
14631         this.page = 0;
14632         this.loadNext = false;
14633         
14634         if(this.isExpanded()){
14635             this.collapse();
14636             if (!this.blockFocus) {
14637                 this.inputEl().focus();
14638             }
14639             
14640         }else {
14641             this.hasFocus = true;
14642             if(this.triggerAction == 'all') {
14643                 this.doQuery(this.allQuery, true);
14644             } else {
14645                 this.doQuery(this.getRawValue());
14646             }
14647             if (!this.blockFocus) {
14648                 this.inputEl().focus();
14649             }
14650         }
14651     },
14652     
14653     onTickableTriggerClick : function(e)
14654     {
14655         if(this.disabled){
14656             return;
14657         }
14658         
14659         this.page = 0;
14660         this.loadNext = false;
14661         this.hasFocus = true;
14662         
14663         if(this.triggerAction == 'all') {
14664             this.doQuery(this.allQuery, true);
14665         } else {
14666             this.doQuery(this.getRawValue());
14667         }
14668     },
14669     
14670     onSearchFieldClick : function(e)
14671     {
14672         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14673             this.onTickableFooterButtonClick(e, false, false);
14674             return;
14675         }
14676         
14677         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14678             return;
14679         }
14680         
14681         this.page = 0;
14682         this.loadNext = false;
14683         this.hasFocus = true;
14684         
14685         if(this.triggerAction == 'all') {
14686             this.doQuery(this.allQuery, true);
14687         } else {
14688             this.doQuery(this.getRawValue());
14689         }
14690     },
14691     
14692     listKeyPress : function(e)
14693     {
14694         //Roo.log('listkeypress');
14695         // scroll to first matching element based on key pres..
14696         if (e.isSpecialKey()) {
14697             return false;
14698         }
14699         var k = String.fromCharCode(e.getKey()).toUpperCase();
14700         //Roo.log(k);
14701         var match  = false;
14702         var csel = this.view.getSelectedNodes();
14703         var cselitem = false;
14704         if (csel.length) {
14705             var ix = this.view.indexOf(csel[0]);
14706             cselitem  = this.store.getAt(ix);
14707             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14708                 cselitem = false;
14709             }
14710             
14711         }
14712         
14713         this.store.each(function(v) { 
14714             if (cselitem) {
14715                 // start at existing selection.
14716                 if (cselitem.id == v.id) {
14717                     cselitem = false;
14718                 }
14719                 return true;
14720             }
14721                 
14722             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14723                 match = this.store.indexOf(v);
14724                 return false;
14725             }
14726             return true;
14727         }, this);
14728         
14729         if (match === false) {
14730             return true; // no more action?
14731         }
14732         // scroll to?
14733         this.view.select(match);
14734         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14735         sn.scrollIntoView(sn.dom.parentNode, false);
14736     },
14737     
14738     onViewScroll : function(e, t){
14739         
14740         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){
14741             return;
14742         }
14743         
14744         this.hasQuery = true;
14745         
14746         this.loading = this.list.select('.loading', true).first();
14747         
14748         if(this.loading === null){
14749             this.list.createChild({
14750                 tag: 'div',
14751                 cls: 'loading roo-select2-more-results roo-select2-active',
14752                 html: 'Loading more results...'
14753             });
14754             
14755             this.loading = this.list.select('.loading', true).first();
14756             
14757             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14758             
14759             this.loading.hide();
14760         }
14761         
14762         this.loading.show();
14763         
14764         var _combo = this;
14765         
14766         this.page++;
14767         this.loadNext = true;
14768         
14769         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14770         
14771         return;
14772     },
14773     
14774     addItem : function(o)
14775     {   
14776         var dv = ''; // display value
14777         
14778         if (this.displayField) {
14779             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14780         } else {
14781             // this is an error condition!!!
14782             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14783         }
14784         
14785         if(!dv.length){
14786             return;
14787         }
14788         
14789         var choice = this.choices.createChild({
14790             tag: 'li',
14791             cls: 'roo-select2-search-choice',
14792             cn: [
14793                 {
14794                     tag: 'div',
14795                     html: dv
14796                 },
14797                 {
14798                     tag: 'a',
14799                     href: '#',
14800                     cls: 'roo-select2-search-choice-close fa fa-times',
14801                     tabindex: '-1'
14802                 }
14803             ]
14804             
14805         }, this.searchField);
14806         
14807         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14808         
14809         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14810         
14811         this.item.push(o);
14812         
14813         this.lastData = o;
14814         
14815         this.syncValue();
14816         
14817         this.inputEl().dom.value = '';
14818         
14819         this.validate();
14820     },
14821     
14822     onRemoveItem : function(e, _self, o)
14823     {
14824         e.preventDefault();
14825         
14826         this.lastItem = Roo.apply([], this.item);
14827         
14828         var index = this.item.indexOf(o.data) * 1;
14829         
14830         if( index < 0){
14831             Roo.log('not this item?!');
14832             return;
14833         }
14834         
14835         this.item.splice(index, 1);
14836         o.item.remove();
14837         
14838         this.syncValue();
14839         
14840         this.fireEvent('remove', this, e);
14841         
14842         this.validate();
14843         
14844     },
14845     
14846     syncValue : function()
14847     {
14848         if(!this.item.length){
14849             this.clearValue();
14850             return;
14851         }
14852             
14853         var value = [];
14854         var _this = this;
14855         Roo.each(this.item, function(i){
14856             if(_this.valueField){
14857                 value.push(i[_this.valueField]);
14858                 return;
14859             }
14860
14861             value.push(i);
14862         });
14863
14864         this.value = value.join(',');
14865
14866         if(this.hiddenField){
14867             this.hiddenField.dom.value = this.value;
14868         }
14869         
14870         this.store.fireEvent("datachanged", this.store);
14871         
14872         this.validate();
14873     },
14874     
14875     clearItem : function()
14876     {
14877         if(!this.multiple){
14878             return;
14879         }
14880         
14881         this.item = [];
14882         
14883         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14884            c.remove();
14885         });
14886         
14887         this.syncValue();
14888         
14889         this.validate();
14890         
14891         if(this.tickable && !Roo.isTouch){
14892             this.view.refresh();
14893         }
14894     },
14895     
14896     inputEl: function ()
14897     {
14898         if(Roo.isIOS && this.useNativeIOS){
14899             return this.el.select('select.roo-ios-select', true).first();
14900         }
14901         
14902         if(Roo.isTouch && this.mobileTouchView){
14903             return this.el.select('input.form-control',true).first();
14904         }
14905         
14906         if(this.tickable){
14907             return this.searchField;
14908         }
14909         
14910         return this.el.select('input.form-control',true).first();
14911     },
14912     
14913     onTickableFooterButtonClick : function(e, btn, el)
14914     {
14915         e.preventDefault();
14916         
14917         this.lastItem = Roo.apply([], this.item);
14918         
14919         if(btn && btn.name == 'cancel'){
14920             this.tickItems = Roo.apply([], this.item);
14921             this.collapse();
14922             return;
14923         }
14924         
14925         this.clearItem();
14926         
14927         var _this = this;
14928         
14929         Roo.each(this.tickItems, function(o){
14930             _this.addItem(o);
14931         });
14932         
14933         this.collapse();
14934         
14935     },
14936     
14937     validate : function()
14938     {
14939         if(this.getVisibilityEl().hasClass('hidden')){
14940             return true;
14941         }
14942         
14943         var v = this.getRawValue();
14944         
14945         if(this.multiple){
14946             v = this.getValue();
14947         }
14948         
14949         if(this.disabled || this.allowBlank || v.length){
14950             this.markValid();
14951             return true;
14952         }
14953         
14954         this.markInvalid();
14955         return false;
14956     },
14957     
14958     tickableInputEl : function()
14959     {
14960         if(!this.tickable || !this.editable){
14961             return this.inputEl();
14962         }
14963         
14964         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14965     },
14966     
14967     
14968     getAutoCreateTouchView : function()
14969     {
14970         var id = Roo.id();
14971         
14972         var cfg = {
14973             cls: 'form-group' //input-group
14974         };
14975         
14976         var input =  {
14977             tag: 'input',
14978             id : id,
14979             type : this.inputType,
14980             cls : 'form-control x-combo-noedit',
14981             autocomplete: 'new-password',
14982             placeholder : this.placeholder || '',
14983             readonly : true
14984         };
14985         
14986         if (this.name) {
14987             input.name = this.name;
14988         }
14989         
14990         if (this.size) {
14991             input.cls += ' input-' + this.size;
14992         }
14993         
14994         if (this.disabled) {
14995             input.disabled = true;
14996         }
14997         
14998         var inputblock = {
14999             cls : '',
15000             cn : [
15001                 input
15002             ]
15003         };
15004         
15005         if(this.before){
15006             inputblock.cls += ' input-group';
15007             
15008             inputblock.cn.unshift({
15009                 tag :'span',
15010                 cls : 'input-group-addon',
15011                 html : this.before
15012             });
15013         }
15014         
15015         if(this.removable && !this.multiple){
15016             inputblock.cls += ' roo-removable';
15017             
15018             inputblock.cn.push({
15019                 tag: 'button',
15020                 html : 'x',
15021                 cls : 'roo-combo-removable-btn close'
15022             });
15023         }
15024
15025         if(this.hasFeedback && !this.allowBlank){
15026             
15027             inputblock.cls += ' has-feedback';
15028             
15029             inputblock.cn.push({
15030                 tag: 'span',
15031                 cls: 'glyphicon form-control-feedback'
15032             });
15033             
15034         }
15035         
15036         if (this.after) {
15037             
15038             inputblock.cls += (this.before) ? '' : ' input-group';
15039             
15040             inputblock.cn.push({
15041                 tag :'span',
15042                 cls : 'input-group-addon',
15043                 html : this.after
15044             });
15045         }
15046
15047         var box = {
15048             tag: 'div',
15049             cn: [
15050                 {
15051                     tag: 'input',
15052                     type : 'hidden',
15053                     cls: 'form-hidden-field'
15054                 },
15055                 inputblock
15056             ]
15057             
15058         };
15059         
15060         if(this.multiple){
15061             box = {
15062                 tag: 'div',
15063                 cn: [
15064                     {
15065                         tag: 'input',
15066                         type : 'hidden',
15067                         cls: 'form-hidden-field'
15068                     },
15069                     {
15070                         tag: 'ul',
15071                         cls: 'roo-select2-choices',
15072                         cn:[
15073                             {
15074                                 tag: 'li',
15075                                 cls: 'roo-select2-search-field',
15076                                 cn: [
15077
15078                                     inputblock
15079                                 ]
15080                             }
15081                         ]
15082                     }
15083                 ]
15084             }
15085         };
15086         
15087         var combobox = {
15088             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15089             cn: [
15090                 box
15091             ]
15092         };
15093         
15094         if(!this.multiple && this.showToggleBtn){
15095             
15096             var caret = {
15097                         tag: 'span',
15098                         cls: 'caret'
15099             };
15100             
15101             if (this.caret != false) {
15102                 caret = {
15103                      tag: 'i',
15104                      cls: 'fa fa-' + this.caret
15105                 };
15106                 
15107             }
15108             
15109             combobox.cn.push({
15110                 tag :'span',
15111                 cls : 'input-group-addon btn dropdown-toggle',
15112                 cn : [
15113                     caret,
15114                     {
15115                         tag: 'span',
15116                         cls: 'combobox-clear',
15117                         cn  : [
15118                             {
15119                                 tag : 'i',
15120                                 cls: 'icon-remove'
15121                             }
15122                         ]
15123                     }
15124                 ]
15125
15126             })
15127         }
15128         
15129         if(this.multiple){
15130             combobox.cls += ' roo-select2-container-multi';
15131         }
15132         
15133         var align = this.labelAlign || this.parentLabelAlign();
15134         
15135         if (align ==='left' && this.fieldLabel.length) {
15136
15137             cfg.cn = [
15138                 {
15139                    tag : 'i',
15140                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15141                    tooltip : 'This field is required'
15142                 },
15143                 {
15144                     tag: 'label',
15145                     cls : 'control-label',
15146                     html : this.fieldLabel
15147
15148                 },
15149                 {
15150                     cls : '', 
15151                     cn: [
15152                         combobox
15153                     ]
15154                 }
15155             ];
15156             
15157             var labelCfg = cfg.cn[1];
15158             var contentCfg = cfg.cn[2];
15159             
15160
15161             if(this.indicatorpos == 'right'){
15162                 cfg.cn = [
15163                     {
15164                         tag: 'label',
15165                         'for' :  id,
15166                         cls : 'control-label',
15167                         cn : [
15168                             {
15169                                 tag : 'span',
15170                                 html : this.fieldLabel
15171                             },
15172                             {
15173                                 tag : 'i',
15174                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15175                                 tooltip : 'This field is required'
15176                             }
15177                         ]
15178                     },
15179                     {
15180                         cls : "",
15181                         cn: [
15182                             combobox
15183                         ]
15184                     }
15185
15186                 ];
15187                 
15188                 labelCfg = cfg.cn[0];
15189                 contentCfg = cfg.cn[1];
15190             }
15191             
15192            
15193             
15194             if(this.labelWidth > 12){
15195                 labelCfg.style = "width: " + this.labelWidth + 'px';
15196             }
15197             
15198             if(this.labelWidth < 13 && this.labelmd == 0){
15199                 this.labelmd = this.labelWidth;
15200             }
15201             
15202             if(this.labellg > 0){
15203                 labelCfg.cls += ' col-lg-' + this.labellg;
15204                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15205             }
15206             
15207             if(this.labelmd > 0){
15208                 labelCfg.cls += ' col-md-' + this.labelmd;
15209                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15210             }
15211             
15212             if(this.labelsm > 0){
15213                 labelCfg.cls += ' col-sm-' + this.labelsm;
15214                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15215             }
15216             
15217             if(this.labelxs > 0){
15218                 labelCfg.cls += ' col-xs-' + this.labelxs;
15219                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15220             }
15221                 
15222                 
15223         } else if ( this.fieldLabel.length) {
15224             cfg.cn = [
15225                 {
15226                    tag : 'i',
15227                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15228                    tooltip : 'This field is required'
15229                 },
15230                 {
15231                     tag: 'label',
15232                     cls : 'control-label',
15233                     html : this.fieldLabel
15234
15235                 },
15236                 {
15237                     cls : '', 
15238                     cn: [
15239                         combobox
15240                     ]
15241                 }
15242             ];
15243             
15244             if(this.indicatorpos == 'right'){
15245                 cfg.cn = [
15246                     {
15247                         tag: 'label',
15248                         cls : 'control-label',
15249                         html : this.fieldLabel,
15250                         cn : [
15251                             {
15252                                tag : 'i',
15253                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15254                                tooltip : 'This field is required'
15255                             }
15256                         ]
15257                     },
15258                     {
15259                         cls : '', 
15260                         cn: [
15261                             combobox
15262                         ]
15263                     }
15264                 ];
15265             }
15266         } else {
15267             cfg.cn = combobox;    
15268         }
15269         
15270         
15271         var settings = this;
15272         
15273         ['xs','sm','md','lg'].map(function(size){
15274             if (settings[size]) {
15275                 cfg.cls += ' col-' + size + '-' + settings[size];
15276             }
15277         });
15278         
15279         return cfg;
15280     },
15281     
15282     initTouchView : function()
15283     {
15284         this.renderTouchView();
15285         
15286         this.touchViewEl.on('scroll', function(){
15287             this.el.dom.scrollTop = 0;
15288         }, this);
15289         
15290         this.originalValue = this.getValue();
15291         
15292         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15293         
15294         this.inputEl().on("click", this.showTouchView, this);
15295         if (this.triggerEl) {
15296             this.triggerEl.on("click", this.showTouchView, this);
15297         }
15298         
15299         
15300         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15301         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15302         
15303         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15304         
15305         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15306         this.store.on('load', this.onTouchViewLoad, this);
15307         this.store.on('loadexception', this.onTouchViewLoadException, this);
15308         
15309         if(this.hiddenName){
15310             
15311             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15312             
15313             this.hiddenField.dom.value =
15314                 this.hiddenValue !== undefined ? this.hiddenValue :
15315                 this.value !== undefined ? this.value : '';
15316         
15317             this.el.dom.removeAttribute('name');
15318             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15319         }
15320         
15321         if(this.multiple){
15322             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15323             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15324         }
15325         
15326         if(this.removable && !this.multiple){
15327             var close = this.closeTriggerEl();
15328             if(close){
15329                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15330                 close.on('click', this.removeBtnClick, this, close);
15331             }
15332         }
15333         /*
15334          * fix the bug in Safari iOS8
15335          */
15336         this.inputEl().on("focus", function(e){
15337             document.activeElement.blur();
15338         }, this);
15339         
15340         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15341         
15342         return;
15343         
15344         
15345     },
15346     
15347     renderTouchView : function()
15348     {
15349         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15350         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15351         
15352         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15353         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15354         
15355         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15356         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15357         this.touchViewBodyEl.setStyle('overflow', 'auto');
15358         
15359         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15360         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15361         
15362         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15363         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15364         
15365     },
15366     
15367     showTouchView : function()
15368     {
15369         if(this.disabled){
15370             return;
15371         }
15372         
15373         this.touchViewHeaderEl.hide();
15374
15375         if(this.modalTitle.length){
15376             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15377             this.touchViewHeaderEl.show();
15378         }
15379
15380         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15381         this.touchViewEl.show();
15382
15383         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15384         
15385         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15386         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15387
15388         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15389
15390         if(this.modalTitle.length){
15391             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15392         }
15393         
15394         this.touchViewBodyEl.setHeight(bodyHeight);
15395
15396         if(this.animate){
15397             var _this = this;
15398             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15399         }else{
15400             this.touchViewEl.addClass('in');
15401         }
15402         
15403         if(this._touchViewMask){
15404             Roo.get(document.body).addClass("x-body-masked");
15405             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15406             this._touchViewMask.setStyle('z-index', 10000);
15407             this._touchViewMask.addClass('show');
15408         }
15409         
15410         this.doTouchViewQuery();
15411         
15412     },
15413     
15414     hideTouchView : function()
15415     {
15416         this.touchViewEl.removeClass('in');
15417
15418         if(this.animate){
15419             var _this = this;
15420             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15421         }else{
15422             this.touchViewEl.setStyle('display', 'none');
15423         }
15424         
15425         if(this._touchViewMask){
15426             this._touchViewMask.removeClass('show');
15427             Roo.get(document.body).removeClass("x-body-masked");
15428         }
15429     },
15430     
15431     setTouchViewValue : function()
15432     {
15433         if(this.multiple){
15434             this.clearItem();
15435         
15436             var _this = this;
15437
15438             Roo.each(this.tickItems, function(o){
15439                 this.addItem(o);
15440             }, this);
15441         }
15442         
15443         this.hideTouchView();
15444     },
15445     
15446     doTouchViewQuery : function()
15447     {
15448         var qe = {
15449             query: '',
15450             forceAll: true,
15451             combo: this,
15452             cancel:false
15453         };
15454         
15455         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15456             return false;
15457         }
15458         
15459         if(!this.alwaysQuery || this.mode == 'local'){
15460             this.onTouchViewLoad();
15461             return;
15462         }
15463         
15464         this.store.load();
15465     },
15466     
15467     onTouchViewBeforeLoad : function(combo,opts)
15468     {
15469         return;
15470     },
15471
15472     // private
15473     onTouchViewLoad : function()
15474     {
15475         if(this.store.getCount() < 1){
15476             this.onTouchViewEmptyResults();
15477             return;
15478         }
15479         
15480         this.clearTouchView();
15481         
15482         var rawValue = this.getRawValue();
15483         
15484         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15485         
15486         this.tickItems = [];
15487         
15488         this.store.data.each(function(d, rowIndex){
15489             var row = this.touchViewListGroup.createChild(template);
15490             
15491             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15492                 row.addClass(d.data.cls);
15493             }
15494             
15495             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15496                 var cfg = {
15497                     data : d.data,
15498                     html : d.data[this.displayField]
15499                 };
15500                 
15501                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15502                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15503                 }
15504             }
15505             row.removeClass('selected');
15506             if(!this.multiple && this.valueField &&
15507                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15508             {
15509                 // radio buttons..
15510                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15511                 row.addClass('selected');
15512             }
15513             
15514             if(this.multiple && this.valueField &&
15515                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15516             {
15517                 
15518                 // checkboxes...
15519                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15520                 this.tickItems.push(d.data);
15521             }
15522             
15523             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15524             
15525         }, this);
15526         
15527         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15528         
15529         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15530
15531         if(this.modalTitle.length){
15532             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15533         }
15534
15535         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15536         
15537         if(this.mobile_restrict_height && listHeight < bodyHeight){
15538             this.touchViewBodyEl.setHeight(listHeight);
15539         }
15540         
15541         var _this = this;
15542         
15543         if(firstChecked && listHeight > bodyHeight){
15544             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15545         }
15546         
15547     },
15548     
15549     onTouchViewLoadException : function()
15550     {
15551         this.hideTouchView();
15552     },
15553     
15554     onTouchViewEmptyResults : function()
15555     {
15556         this.clearTouchView();
15557         
15558         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15559         
15560         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15561         
15562     },
15563     
15564     clearTouchView : function()
15565     {
15566         this.touchViewListGroup.dom.innerHTML = '';
15567     },
15568     
15569     onTouchViewClick : function(e, el, o)
15570     {
15571         e.preventDefault();
15572         
15573         var row = o.row;
15574         var rowIndex = o.rowIndex;
15575         
15576         var r = this.store.getAt(rowIndex);
15577         
15578         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15579             
15580             if(!this.multiple){
15581                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15582                     c.dom.removeAttribute('checked');
15583                 }, this);
15584
15585                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15586
15587                 this.setFromData(r.data);
15588
15589                 var close = this.closeTriggerEl();
15590
15591                 if(close){
15592                     close.show();
15593                 }
15594
15595                 this.hideTouchView();
15596
15597                 this.fireEvent('select', this, r, rowIndex);
15598
15599                 return;
15600             }
15601
15602             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15603                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15604                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15605                 return;
15606             }
15607
15608             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15609             this.addItem(r.data);
15610             this.tickItems.push(r.data);
15611         }
15612     },
15613     
15614     getAutoCreateNativeIOS : function()
15615     {
15616         var cfg = {
15617             cls: 'form-group' //input-group,
15618         };
15619         
15620         var combobox =  {
15621             tag: 'select',
15622             cls : 'roo-ios-select'
15623         };
15624         
15625         if (this.name) {
15626             combobox.name = this.name;
15627         }
15628         
15629         if (this.disabled) {
15630             combobox.disabled = true;
15631         }
15632         
15633         var settings = this;
15634         
15635         ['xs','sm','md','lg'].map(function(size){
15636             if (settings[size]) {
15637                 cfg.cls += ' col-' + size + '-' + settings[size];
15638             }
15639         });
15640         
15641         cfg.cn = combobox;
15642         
15643         return cfg;
15644         
15645     },
15646     
15647     initIOSView : function()
15648     {
15649         this.store.on('load', this.onIOSViewLoad, this);
15650         
15651         return;
15652     },
15653     
15654     onIOSViewLoad : function()
15655     {
15656         if(this.store.getCount() < 1){
15657             return;
15658         }
15659         
15660         this.clearIOSView();
15661         
15662         if(this.allowBlank) {
15663             
15664             var default_text = '-- SELECT --';
15665             
15666             if(this.placeholder.length){
15667                 default_text = this.placeholder;
15668             }
15669             
15670             if(this.emptyTitle.length){
15671                 default_text += ' - ' + this.emptyTitle + ' -';
15672             }
15673             
15674             var opt = this.inputEl().createChild({
15675                 tag: 'option',
15676                 value : 0,
15677                 html : default_text
15678             });
15679             
15680             var o = {};
15681             o[this.valueField] = 0;
15682             o[this.displayField] = default_text;
15683             
15684             this.ios_options.push({
15685                 data : o,
15686                 el : opt
15687             });
15688             
15689         }
15690         
15691         this.store.data.each(function(d, rowIndex){
15692             
15693             var html = '';
15694             
15695             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15696                 html = d.data[this.displayField];
15697             }
15698             
15699             var value = '';
15700             
15701             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15702                 value = d.data[this.valueField];
15703             }
15704             
15705             var option = {
15706                 tag: 'option',
15707                 value : value,
15708                 html : html
15709             };
15710             
15711             if(this.value == d.data[this.valueField]){
15712                 option['selected'] = true;
15713             }
15714             
15715             var opt = this.inputEl().createChild(option);
15716             
15717             this.ios_options.push({
15718                 data : d.data,
15719                 el : opt
15720             });
15721             
15722         }, this);
15723         
15724         this.inputEl().on('change', function(){
15725            this.fireEvent('select', this);
15726         }, this);
15727         
15728     },
15729     
15730     clearIOSView: function()
15731     {
15732         this.inputEl().dom.innerHTML = '';
15733         
15734         this.ios_options = [];
15735     },
15736     
15737     setIOSValue: function(v)
15738     {
15739         this.value = v;
15740         
15741         if(!this.ios_options){
15742             return;
15743         }
15744         
15745         Roo.each(this.ios_options, function(opts){
15746            
15747            opts.el.dom.removeAttribute('selected');
15748            
15749            if(opts.data[this.valueField] != v){
15750                return;
15751            }
15752            
15753            opts.el.dom.setAttribute('selected', true);
15754            
15755         }, this);
15756     }
15757
15758     /** 
15759     * @cfg {Boolean} grow 
15760     * @hide 
15761     */
15762     /** 
15763     * @cfg {Number} growMin 
15764     * @hide 
15765     */
15766     /** 
15767     * @cfg {Number} growMax 
15768     * @hide 
15769     */
15770     /**
15771      * @hide
15772      * @method autoSize
15773      */
15774 });
15775
15776 Roo.apply(Roo.bootstrap.ComboBox,  {
15777     
15778     header : {
15779         tag: 'div',
15780         cls: 'modal-header',
15781         cn: [
15782             {
15783                 tag: 'h4',
15784                 cls: 'modal-title'
15785             }
15786         ]
15787     },
15788     
15789     body : {
15790         tag: 'div',
15791         cls: 'modal-body',
15792         cn: [
15793             {
15794                 tag: 'ul',
15795                 cls: 'list-group'
15796             }
15797         ]
15798     },
15799     
15800     listItemRadio : {
15801         tag: 'li',
15802         cls: 'list-group-item',
15803         cn: [
15804             {
15805                 tag: 'span',
15806                 cls: 'roo-combobox-list-group-item-value'
15807             },
15808             {
15809                 tag: 'div',
15810                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15811                 cn: [
15812                     {
15813                         tag: 'input',
15814                         type: 'radio'
15815                     },
15816                     {
15817                         tag: 'label'
15818                     }
15819                 ]
15820             }
15821         ]
15822     },
15823     
15824     listItemCheckbox : {
15825         tag: 'li',
15826         cls: 'list-group-item',
15827         cn: [
15828             {
15829                 tag: 'span',
15830                 cls: 'roo-combobox-list-group-item-value'
15831             },
15832             {
15833                 tag: 'div',
15834                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15835                 cn: [
15836                     {
15837                         tag: 'input',
15838                         type: 'checkbox'
15839                     },
15840                     {
15841                         tag: 'label'
15842                     }
15843                 ]
15844             }
15845         ]
15846     },
15847     
15848     emptyResult : {
15849         tag: 'div',
15850         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15851     },
15852     
15853     footer : {
15854         tag: 'div',
15855         cls: 'modal-footer',
15856         cn: [
15857             {
15858                 tag: 'div',
15859                 cls: 'row',
15860                 cn: [
15861                     {
15862                         tag: 'div',
15863                         cls: 'col-xs-6 text-left',
15864                         cn: {
15865                             tag: 'button',
15866                             cls: 'btn btn-danger roo-touch-view-cancel',
15867                             html: 'Cancel'
15868                         }
15869                     },
15870                     {
15871                         tag: 'div',
15872                         cls: 'col-xs-6 text-right',
15873                         cn: {
15874                             tag: 'button',
15875                             cls: 'btn btn-success roo-touch-view-ok',
15876                             html: 'OK'
15877                         }
15878                     }
15879                 ]
15880             }
15881         ]
15882         
15883     }
15884 });
15885
15886 Roo.apply(Roo.bootstrap.ComboBox,  {
15887     
15888     touchViewTemplate : {
15889         tag: 'div',
15890         cls: 'modal fade roo-combobox-touch-view',
15891         cn: [
15892             {
15893                 tag: 'div',
15894                 cls: 'modal-dialog',
15895                 style : 'position:fixed', // we have to fix position....
15896                 cn: [
15897                     {
15898                         tag: 'div',
15899                         cls: 'modal-content',
15900                         cn: [
15901                             Roo.bootstrap.ComboBox.header,
15902                             Roo.bootstrap.ComboBox.body,
15903                             Roo.bootstrap.ComboBox.footer
15904                         ]
15905                     }
15906                 ]
15907             }
15908         ]
15909     }
15910 });/*
15911  * Based on:
15912  * Ext JS Library 1.1.1
15913  * Copyright(c) 2006-2007, Ext JS, LLC.
15914  *
15915  * Originally Released Under LGPL - original licence link has changed is not relivant.
15916  *
15917  * Fork - LGPL
15918  * <script type="text/javascript">
15919  */
15920
15921 /**
15922  * @class Roo.View
15923  * @extends Roo.util.Observable
15924  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15925  * This class also supports single and multi selection modes. <br>
15926  * Create a data model bound view:
15927  <pre><code>
15928  var store = new Roo.data.Store(...);
15929
15930  var view = new Roo.View({
15931     el : "my-element",
15932     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15933  
15934     singleSelect: true,
15935     selectedClass: "ydataview-selected",
15936     store: store
15937  });
15938
15939  // listen for node click?
15940  view.on("click", function(vw, index, node, e){
15941  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15942  });
15943
15944  // load XML data
15945  dataModel.load("foobar.xml");
15946  </code></pre>
15947  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15948  * <br><br>
15949  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15950  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15951  * 
15952  * Note: old style constructor is still suported (container, template, config)
15953  * 
15954  * @constructor
15955  * Create a new View
15956  * @param {Object} config The config object
15957  * 
15958  */
15959 Roo.View = function(config, depreciated_tpl, depreciated_config){
15960     
15961     this.parent = false;
15962     
15963     if (typeof(depreciated_tpl) == 'undefined') {
15964         // new way.. - universal constructor.
15965         Roo.apply(this, config);
15966         this.el  = Roo.get(this.el);
15967     } else {
15968         // old format..
15969         this.el  = Roo.get(config);
15970         this.tpl = depreciated_tpl;
15971         Roo.apply(this, depreciated_config);
15972     }
15973     this.wrapEl  = this.el.wrap().wrap();
15974     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15975     
15976     
15977     if(typeof(this.tpl) == "string"){
15978         this.tpl = new Roo.Template(this.tpl);
15979     } else {
15980         // support xtype ctors..
15981         this.tpl = new Roo.factory(this.tpl, Roo);
15982     }
15983     
15984     
15985     this.tpl.compile();
15986     
15987     /** @private */
15988     this.addEvents({
15989         /**
15990          * @event beforeclick
15991          * Fires before a click is processed. Returns false to cancel the default action.
15992          * @param {Roo.View} this
15993          * @param {Number} index The index of the target node
15994          * @param {HTMLElement} node The target node
15995          * @param {Roo.EventObject} e The raw event object
15996          */
15997             "beforeclick" : true,
15998         /**
15999          * @event click
16000          * Fires when a template node is clicked.
16001          * @param {Roo.View} this
16002          * @param {Number} index The index of the target node
16003          * @param {HTMLElement} node The target node
16004          * @param {Roo.EventObject} e The raw event object
16005          */
16006             "click" : true,
16007         /**
16008          * @event dblclick
16009          * Fires when a template node is double clicked.
16010          * @param {Roo.View} this
16011          * @param {Number} index The index of the target node
16012          * @param {HTMLElement} node The target node
16013          * @param {Roo.EventObject} e The raw event object
16014          */
16015             "dblclick" : true,
16016         /**
16017          * @event contextmenu
16018          * Fires when a template node is right clicked.
16019          * @param {Roo.View} this
16020          * @param {Number} index The index of the target node
16021          * @param {HTMLElement} node The target node
16022          * @param {Roo.EventObject} e The raw event object
16023          */
16024             "contextmenu" : true,
16025         /**
16026          * @event selectionchange
16027          * Fires when the selected nodes change.
16028          * @param {Roo.View} this
16029          * @param {Array} selections Array of the selected nodes
16030          */
16031             "selectionchange" : true,
16032     
16033         /**
16034          * @event beforeselect
16035          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16036          * @param {Roo.View} this
16037          * @param {HTMLElement} node The node to be selected
16038          * @param {Array} selections Array of currently selected nodes
16039          */
16040             "beforeselect" : true,
16041         /**
16042          * @event preparedata
16043          * Fires on every row to render, to allow you to change the data.
16044          * @param {Roo.View} this
16045          * @param {Object} data to be rendered (change this)
16046          */
16047           "preparedata" : true
16048           
16049           
16050         });
16051
16052
16053
16054     this.el.on({
16055         "click": this.onClick,
16056         "dblclick": this.onDblClick,
16057         "contextmenu": this.onContextMenu,
16058         scope:this
16059     });
16060
16061     this.selections = [];
16062     this.nodes = [];
16063     this.cmp = new Roo.CompositeElementLite([]);
16064     if(this.store){
16065         this.store = Roo.factory(this.store, Roo.data);
16066         this.setStore(this.store, true);
16067     }
16068     
16069     if ( this.footer && this.footer.xtype) {
16070            
16071          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16072         
16073         this.footer.dataSource = this.store;
16074         this.footer.container = fctr;
16075         this.footer = Roo.factory(this.footer, Roo);
16076         fctr.insertFirst(this.el);
16077         
16078         // this is a bit insane - as the paging toolbar seems to detach the el..
16079 //        dom.parentNode.parentNode.parentNode
16080          // they get detached?
16081     }
16082     
16083     
16084     Roo.View.superclass.constructor.call(this);
16085     
16086     
16087 };
16088
16089 Roo.extend(Roo.View, Roo.util.Observable, {
16090     
16091      /**
16092      * @cfg {Roo.data.Store} store Data store to load data from.
16093      */
16094     store : false,
16095     
16096     /**
16097      * @cfg {String|Roo.Element} el The container element.
16098      */
16099     el : '',
16100     
16101     /**
16102      * @cfg {String|Roo.Template} tpl The template used by this View 
16103      */
16104     tpl : false,
16105     /**
16106      * @cfg {String} dataName the named area of the template to use as the data area
16107      *                          Works with domtemplates roo-name="name"
16108      */
16109     dataName: false,
16110     /**
16111      * @cfg {String} selectedClass The css class to add to selected nodes
16112      */
16113     selectedClass : "x-view-selected",
16114      /**
16115      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16116      */
16117     emptyText : "",
16118     
16119     /**
16120      * @cfg {String} text to display on mask (default Loading)
16121      */
16122     mask : false,
16123     /**
16124      * @cfg {Boolean} multiSelect Allow multiple selection
16125      */
16126     multiSelect : false,
16127     /**
16128      * @cfg {Boolean} singleSelect Allow single selection
16129      */
16130     singleSelect:  false,
16131     
16132     /**
16133      * @cfg {Boolean} toggleSelect - selecting 
16134      */
16135     toggleSelect : false,
16136     
16137     /**
16138      * @cfg {Boolean} tickable - selecting 
16139      */
16140     tickable : false,
16141     
16142     /**
16143      * Returns the element this view is bound to.
16144      * @return {Roo.Element}
16145      */
16146     getEl : function(){
16147         return this.wrapEl;
16148     },
16149     
16150     
16151
16152     /**
16153      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16154      */
16155     refresh : function(){
16156         //Roo.log('refresh');
16157         var t = this.tpl;
16158         
16159         // if we are using something like 'domtemplate', then
16160         // the what gets used is:
16161         // t.applySubtemplate(NAME, data, wrapping data..)
16162         // the outer template then get' applied with
16163         //     the store 'extra data'
16164         // and the body get's added to the
16165         //      roo-name="data" node?
16166         //      <span class='roo-tpl-{name}'></span> ?????
16167         
16168         
16169         
16170         this.clearSelections();
16171         this.el.update("");
16172         var html = [];
16173         var records = this.store.getRange();
16174         if(records.length < 1) {
16175             
16176             // is this valid??  = should it render a template??
16177             
16178             this.el.update(this.emptyText);
16179             return;
16180         }
16181         var el = this.el;
16182         if (this.dataName) {
16183             this.el.update(t.apply(this.store.meta)); //????
16184             el = this.el.child('.roo-tpl-' + this.dataName);
16185         }
16186         
16187         for(var i = 0, len = records.length; i < len; i++){
16188             var data = this.prepareData(records[i].data, i, records[i]);
16189             this.fireEvent("preparedata", this, data, i, records[i]);
16190             
16191             var d = Roo.apply({}, data);
16192             
16193             if(this.tickable){
16194                 Roo.apply(d, {'roo-id' : Roo.id()});
16195                 
16196                 var _this = this;
16197             
16198                 Roo.each(this.parent.item, function(item){
16199                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16200                         return;
16201                     }
16202                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16203                 });
16204             }
16205             
16206             html[html.length] = Roo.util.Format.trim(
16207                 this.dataName ?
16208                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16209                     t.apply(d)
16210             );
16211         }
16212         
16213         
16214         
16215         el.update(html.join(""));
16216         this.nodes = el.dom.childNodes;
16217         this.updateIndexes(0);
16218     },
16219     
16220
16221     /**
16222      * Function to override to reformat the data that is sent to
16223      * the template for each node.
16224      * DEPRICATED - use the preparedata event handler.
16225      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16226      * a JSON object for an UpdateManager bound view).
16227      */
16228     prepareData : function(data, index, record)
16229     {
16230         this.fireEvent("preparedata", this, data, index, record);
16231         return data;
16232     },
16233
16234     onUpdate : function(ds, record){
16235         // Roo.log('on update');   
16236         this.clearSelections();
16237         var index = this.store.indexOf(record);
16238         var n = this.nodes[index];
16239         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16240         n.parentNode.removeChild(n);
16241         this.updateIndexes(index, index);
16242     },
16243
16244     
16245     
16246 // --------- FIXME     
16247     onAdd : function(ds, records, index)
16248     {
16249         //Roo.log(['on Add', ds, records, index] );        
16250         this.clearSelections();
16251         if(this.nodes.length == 0){
16252             this.refresh();
16253             return;
16254         }
16255         var n = this.nodes[index];
16256         for(var i = 0, len = records.length; i < len; i++){
16257             var d = this.prepareData(records[i].data, i, records[i]);
16258             if(n){
16259                 this.tpl.insertBefore(n, d);
16260             }else{
16261                 
16262                 this.tpl.append(this.el, d);
16263             }
16264         }
16265         this.updateIndexes(index);
16266     },
16267
16268     onRemove : function(ds, record, index){
16269        // Roo.log('onRemove');
16270         this.clearSelections();
16271         var el = this.dataName  ?
16272             this.el.child('.roo-tpl-' + this.dataName) :
16273             this.el; 
16274         
16275         el.dom.removeChild(this.nodes[index]);
16276         this.updateIndexes(index);
16277     },
16278
16279     /**
16280      * Refresh an individual node.
16281      * @param {Number} index
16282      */
16283     refreshNode : function(index){
16284         this.onUpdate(this.store, this.store.getAt(index));
16285     },
16286
16287     updateIndexes : function(startIndex, endIndex){
16288         var ns = this.nodes;
16289         startIndex = startIndex || 0;
16290         endIndex = endIndex || ns.length - 1;
16291         for(var i = startIndex; i <= endIndex; i++){
16292             ns[i].nodeIndex = i;
16293         }
16294     },
16295
16296     /**
16297      * Changes the data store this view uses and refresh the view.
16298      * @param {Store} store
16299      */
16300     setStore : function(store, initial){
16301         if(!initial && this.store){
16302             this.store.un("datachanged", this.refresh);
16303             this.store.un("add", this.onAdd);
16304             this.store.un("remove", this.onRemove);
16305             this.store.un("update", this.onUpdate);
16306             this.store.un("clear", this.refresh);
16307             this.store.un("beforeload", this.onBeforeLoad);
16308             this.store.un("load", this.onLoad);
16309             this.store.un("loadexception", this.onLoad);
16310         }
16311         if(store){
16312           
16313             store.on("datachanged", this.refresh, this);
16314             store.on("add", this.onAdd, this);
16315             store.on("remove", this.onRemove, this);
16316             store.on("update", this.onUpdate, this);
16317             store.on("clear", this.refresh, this);
16318             store.on("beforeload", this.onBeforeLoad, this);
16319             store.on("load", this.onLoad, this);
16320             store.on("loadexception", this.onLoad, this);
16321         }
16322         
16323         if(store){
16324             this.refresh();
16325         }
16326     },
16327     /**
16328      * onbeforeLoad - masks the loading area.
16329      *
16330      */
16331     onBeforeLoad : function(store,opts)
16332     {
16333          //Roo.log('onBeforeLoad');   
16334         if (!opts.add) {
16335             this.el.update("");
16336         }
16337         this.el.mask(this.mask ? this.mask : "Loading" ); 
16338     },
16339     onLoad : function ()
16340     {
16341         this.el.unmask();
16342     },
16343     
16344
16345     /**
16346      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16347      * @param {HTMLElement} node
16348      * @return {HTMLElement} The template node
16349      */
16350     findItemFromChild : function(node){
16351         var el = this.dataName  ?
16352             this.el.child('.roo-tpl-' + this.dataName,true) :
16353             this.el.dom; 
16354         
16355         if(!node || node.parentNode == el){
16356                     return node;
16357             }
16358             var p = node.parentNode;
16359             while(p && p != el){
16360             if(p.parentNode == el){
16361                 return p;
16362             }
16363             p = p.parentNode;
16364         }
16365             return null;
16366     },
16367
16368     /** @ignore */
16369     onClick : function(e){
16370         var item = this.findItemFromChild(e.getTarget());
16371         if(item){
16372             var index = this.indexOf(item);
16373             if(this.onItemClick(item, index, e) !== false){
16374                 this.fireEvent("click", this, index, item, e);
16375             }
16376         }else{
16377             this.clearSelections();
16378         }
16379     },
16380
16381     /** @ignore */
16382     onContextMenu : function(e){
16383         var item = this.findItemFromChild(e.getTarget());
16384         if(item){
16385             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16386         }
16387     },
16388
16389     /** @ignore */
16390     onDblClick : function(e){
16391         var item = this.findItemFromChild(e.getTarget());
16392         if(item){
16393             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16394         }
16395     },
16396
16397     onItemClick : function(item, index, e)
16398     {
16399         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16400             return false;
16401         }
16402         if (this.toggleSelect) {
16403             var m = this.isSelected(item) ? 'unselect' : 'select';
16404             //Roo.log(m);
16405             var _t = this;
16406             _t[m](item, true, false);
16407             return true;
16408         }
16409         if(this.multiSelect || this.singleSelect){
16410             if(this.multiSelect && e.shiftKey && this.lastSelection){
16411                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16412             }else{
16413                 this.select(item, this.multiSelect && e.ctrlKey);
16414                 this.lastSelection = item;
16415             }
16416             
16417             if(!this.tickable){
16418                 e.preventDefault();
16419             }
16420             
16421         }
16422         return true;
16423     },
16424
16425     /**
16426      * Get the number of selected nodes.
16427      * @return {Number}
16428      */
16429     getSelectionCount : function(){
16430         return this.selections.length;
16431     },
16432
16433     /**
16434      * Get the currently selected nodes.
16435      * @return {Array} An array of HTMLElements
16436      */
16437     getSelectedNodes : function(){
16438         return this.selections;
16439     },
16440
16441     /**
16442      * Get the indexes of the selected nodes.
16443      * @return {Array}
16444      */
16445     getSelectedIndexes : function(){
16446         var indexes = [], s = this.selections;
16447         for(var i = 0, len = s.length; i < len; i++){
16448             indexes.push(s[i].nodeIndex);
16449         }
16450         return indexes;
16451     },
16452
16453     /**
16454      * Clear all selections
16455      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16456      */
16457     clearSelections : function(suppressEvent){
16458         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16459             this.cmp.elements = this.selections;
16460             this.cmp.removeClass(this.selectedClass);
16461             this.selections = [];
16462             if(!suppressEvent){
16463                 this.fireEvent("selectionchange", this, this.selections);
16464             }
16465         }
16466     },
16467
16468     /**
16469      * Returns true if the passed node is selected
16470      * @param {HTMLElement/Number} node The node or node index
16471      * @return {Boolean}
16472      */
16473     isSelected : function(node){
16474         var s = this.selections;
16475         if(s.length < 1){
16476             return false;
16477         }
16478         node = this.getNode(node);
16479         return s.indexOf(node) !== -1;
16480     },
16481
16482     /**
16483      * Selects nodes.
16484      * @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
16485      * @param {Boolean} keepExisting (optional) true to keep existing selections
16486      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16487      */
16488     select : function(nodeInfo, keepExisting, suppressEvent){
16489         if(nodeInfo instanceof Array){
16490             if(!keepExisting){
16491                 this.clearSelections(true);
16492             }
16493             for(var i = 0, len = nodeInfo.length; i < len; i++){
16494                 this.select(nodeInfo[i], true, true);
16495             }
16496             return;
16497         } 
16498         var node = this.getNode(nodeInfo);
16499         if(!node || this.isSelected(node)){
16500             return; // already selected.
16501         }
16502         if(!keepExisting){
16503             this.clearSelections(true);
16504         }
16505         
16506         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16507             Roo.fly(node).addClass(this.selectedClass);
16508             this.selections.push(node);
16509             if(!suppressEvent){
16510                 this.fireEvent("selectionchange", this, this.selections);
16511             }
16512         }
16513         
16514         
16515     },
16516       /**
16517      * Unselects nodes.
16518      * @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
16519      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16520      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16521      */
16522     unselect : function(nodeInfo, keepExisting, suppressEvent)
16523     {
16524         if(nodeInfo instanceof Array){
16525             Roo.each(this.selections, function(s) {
16526                 this.unselect(s, nodeInfo);
16527             }, this);
16528             return;
16529         }
16530         var node = this.getNode(nodeInfo);
16531         if(!node || !this.isSelected(node)){
16532             //Roo.log("not selected");
16533             return; // not selected.
16534         }
16535         // fireevent???
16536         var ns = [];
16537         Roo.each(this.selections, function(s) {
16538             if (s == node ) {
16539                 Roo.fly(node).removeClass(this.selectedClass);
16540
16541                 return;
16542             }
16543             ns.push(s);
16544         },this);
16545         
16546         this.selections= ns;
16547         this.fireEvent("selectionchange", this, this.selections);
16548     },
16549
16550     /**
16551      * Gets a template node.
16552      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16553      * @return {HTMLElement} The node or null if it wasn't found
16554      */
16555     getNode : function(nodeInfo){
16556         if(typeof nodeInfo == "string"){
16557             return document.getElementById(nodeInfo);
16558         }else if(typeof nodeInfo == "number"){
16559             return this.nodes[nodeInfo];
16560         }
16561         return nodeInfo;
16562     },
16563
16564     /**
16565      * Gets a range template nodes.
16566      * @param {Number} startIndex
16567      * @param {Number} endIndex
16568      * @return {Array} An array of nodes
16569      */
16570     getNodes : function(start, end){
16571         var ns = this.nodes;
16572         start = start || 0;
16573         end = typeof end == "undefined" ? ns.length - 1 : end;
16574         var nodes = [];
16575         if(start <= end){
16576             for(var i = start; i <= end; i++){
16577                 nodes.push(ns[i]);
16578             }
16579         } else{
16580             for(var i = start; i >= end; i--){
16581                 nodes.push(ns[i]);
16582             }
16583         }
16584         return nodes;
16585     },
16586
16587     /**
16588      * Finds the index of the passed node
16589      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16590      * @return {Number} The index of the node or -1
16591      */
16592     indexOf : function(node){
16593         node = this.getNode(node);
16594         if(typeof node.nodeIndex == "number"){
16595             return node.nodeIndex;
16596         }
16597         var ns = this.nodes;
16598         for(var i = 0, len = ns.length; i < len; i++){
16599             if(ns[i] == node){
16600                 return i;
16601             }
16602         }
16603         return -1;
16604     }
16605 });
16606 /*
16607  * - LGPL
16608  *
16609  * based on jquery fullcalendar
16610  * 
16611  */
16612
16613 Roo.bootstrap = Roo.bootstrap || {};
16614 /**
16615  * @class Roo.bootstrap.Calendar
16616  * @extends Roo.bootstrap.Component
16617  * Bootstrap Calendar class
16618  * @cfg {Boolean} loadMask (true|false) default false
16619  * @cfg {Object} header generate the user specific header of the calendar, default false
16620
16621  * @constructor
16622  * Create a new Container
16623  * @param {Object} config The config object
16624  */
16625
16626
16627
16628 Roo.bootstrap.Calendar = function(config){
16629     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16630      this.addEvents({
16631         /**
16632              * @event select
16633              * Fires when a date is selected
16634              * @param {DatePicker} this
16635              * @param {Date} date The selected date
16636              */
16637         'select': true,
16638         /**
16639              * @event monthchange
16640              * Fires when the displayed month changes 
16641              * @param {DatePicker} this
16642              * @param {Date} date The selected month
16643              */
16644         'monthchange': true,
16645         /**
16646              * @event evententer
16647              * Fires when mouse over an event
16648              * @param {Calendar} this
16649              * @param {event} Event
16650              */
16651         'evententer': true,
16652         /**
16653              * @event eventleave
16654              * Fires when the mouse leaves an
16655              * @param {Calendar} this
16656              * @param {event}
16657              */
16658         'eventleave': true,
16659         /**
16660              * @event eventclick
16661              * Fires when the mouse click an
16662              * @param {Calendar} this
16663              * @param {event}
16664              */
16665         'eventclick': true
16666         
16667     });
16668
16669 };
16670
16671 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16672     
16673      /**
16674      * @cfg {Number} startDay
16675      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16676      */
16677     startDay : 0,
16678     
16679     loadMask : false,
16680     
16681     header : false,
16682       
16683     getAutoCreate : function(){
16684         
16685         
16686         var fc_button = function(name, corner, style, content ) {
16687             return Roo.apply({},{
16688                 tag : 'span',
16689                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16690                          (corner.length ?
16691                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16692                             ''
16693                         ),
16694                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16695                 unselectable: 'on'
16696             });
16697         };
16698         
16699         var header = {};
16700         
16701         if(!this.header){
16702             header = {
16703                 tag : 'table',
16704                 cls : 'fc-header',
16705                 style : 'width:100%',
16706                 cn : [
16707                     {
16708                         tag: 'tr',
16709                         cn : [
16710                             {
16711                                 tag : 'td',
16712                                 cls : 'fc-header-left',
16713                                 cn : [
16714                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16715                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16716                                     { tag: 'span', cls: 'fc-header-space' },
16717                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16718
16719
16720                                 ]
16721                             },
16722
16723                             {
16724                                 tag : 'td',
16725                                 cls : 'fc-header-center',
16726                                 cn : [
16727                                     {
16728                                         tag: 'span',
16729                                         cls: 'fc-header-title',
16730                                         cn : {
16731                                             tag: 'H2',
16732                                             html : 'month / year'
16733                                         }
16734                                     }
16735
16736                                 ]
16737                             },
16738                             {
16739                                 tag : 'td',
16740                                 cls : 'fc-header-right',
16741                                 cn : [
16742                               /*      fc_button('month', 'left', '', 'month' ),
16743                                     fc_button('week', '', '', 'week' ),
16744                                     fc_button('day', 'right', '', 'day' )
16745                                 */    
16746
16747                                 ]
16748                             }
16749
16750                         ]
16751                     }
16752                 ]
16753             };
16754         }
16755         
16756         header = this.header;
16757         
16758        
16759         var cal_heads = function() {
16760             var ret = [];
16761             // fixme - handle this.
16762             
16763             for (var i =0; i < Date.dayNames.length; i++) {
16764                 var d = Date.dayNames[i];
16765                 ret.push({
16766                     tag: 'th',
16767                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16768                     html : d.substring(0,3)
16769                 });
16770                 
16771             }
16772             ret[0].cls += ' fc-first';
16773             ret[6].cls += ' fc-last';
16774             return ret;
16775         };
16776         var cal_cell = function(n) {
16777             return  {
16778                 tag: 'td',
16779                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16780                 cn : [
16781                     {
16782                         cn : [
16783                             {
16784                                 cls: 'fc-day-number',
16785                                 html: 'D'
16786                             },
16787                             {
16788                                 cls: 'fc-day-content',
16789                              
16790                                 cn : [
16791                                      {
16792                                         style: 'position: relative;' // height: 17px;
16793                                     }
16794                                 ]
16795                             }
16796                             
16797                             
16798                         ]
16799                     }
16800                 ]
16801                 
16802             }
16803         };
16804         var cal_rows = function() {
16805             
16806             var ret = [];
16807             for (var r = 0; r < 6; r++) {
16808                 var row= {
16809                     tag : 'tr',
16810                     cls : 'fc-week',
16811                     cn : []
16812                 };
16813                 
16814                 for (var i =0; i < Date.dayNames.length; i++) {
16815                     var d = Date.dayNames[i];
16816                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16817
16818                 }
16819                 row.cn[0].cls+=' fc-first';
16820                 row.cn[0].cn[0].style = 'min-height:90px';
16821                 row.cn[6].cls+=' fc-last';
16822                 ret.push(row);
16823                 
16824             }
16825             ret[0].cls += ' fc-first';
16826             ret[4].cls += ' fc-prev-last';
16827             ret[5].cls += ' fc-last';
16828             return ret;
16829             
16830         };
16831         
16832         var cal_table = {
16833             tag: 'table',
16834             cls: 'fc-border-separate',
16835             style : 'width:100%',
16836             cellspacing  : 0,
16837             cn : [
16838                 { 
16839                     tag: 'thead',
16840                     cn : [
16841                         { 
16842                             tag: 'tr',
16843                             cls : 'fc-first fc-last',
16844                             cn : cal_heads()
16845                         }
16846                     ]
16847                 },
16848                 { 
16849                     tag: 'tbody',
16850                     cn : cal_rows()
16851                 }
16852                   
16853             ]
16854         };
16855          
16856          var cfg = {
16857             cls : 'fc fc-ltr',
16858             cn : [
16859                 header,
16860                 {
16861                     cls : 'fc-content',
16862                     style : "position: relative;",
16863                     cn : [
16864                         {
16865                             cls : 'fc-view fc-view-month fc-grid',
16866                             style : 'position: relative',
16867                             unselectable : 'on',
16868                             cn : [
16869                                 {
16870                                     cls : 'fc-event-container',
16871                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16872                                 },
16873                                 cal_table
16874                             ]
16875                         }
16876                     ]
16877     
16878                 }
16879            ] 
16880             
16881         };
16882         
16883          
16884         
16885         return cfg;
16886     },
16887     
16888     
16889     initEvents : function()
16890     {
16891         if(!this.store){
16892             throw "can not find store for calendar";
16893         }
16894         
16895         var mark = {
16896             tag: "div",
16897             cls:"x-dlg-mask",
16898             style: "text-align:center",
16899             cn: [
16900                 {
16901                     tag: "div",
16902                     style: "background-color:white;width:50%;margin:250 auto",
16903                     cn: [
16904                         {
16905                             tag: "img",
16906                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16907                         },
16908                         {
16909                             tag: "span",
16910                             html: "Loading"
16911                         }
16912                         
16913                     ]
16914                 }
16915             ]
16916         };
16917         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16918         
16919         var size = this.el.select('.fc-content', true).first().getSize();
16920         this.maskEl.setSize(size.width, size.height);
16921         this.maskEl.enableDisplayMode("block");
16922         if(!this.loadMask){
16923             this.maskEl.hide();
16924         }
16925         
16926         this.store = Roo.factory(this.store, Roo.data);
16927         this.store.on('load', this.onLoad, this);
16928         this.store.on('beforeload', this.onBeforeLoad, this);
16929         
16930         this.resize();
16931         
16932         this.cells = this.el.select('.fc-day',true);
16933         //Roo.log(this.cells);
16934         this.textNodes = this.el.query('.fc-day-number');
16935         this.cells.addClassOnOver('fc-state-hover');
16936         
16937         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16938         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16939         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16940         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16941         
16942         this.on('monthchange', this.onMonthChange, this);
16943         
16944         this.update(new Date().clearTime());
16945     },
16946     
16947     resize : function() {
16948         var sz  = this.el.getSize();
16949         
16950         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16951         this.el.select('.fc-day-content div',true).setHeight(34);
16952     },
16953     
16954     
16955     // private
16956     showPrevMonth : function(e){
16957         this.update(this.activeDate.add("mo", -1));
16958     },
16959     showToday : function(e){
16960         this.update(new Date().clearTime());
16961     },
16962     // private
16963     showNextMonth : function(e){
16964         this.update(this.activeDate.add("mo", 1));
16965     },
16966
16967     // private
16968     showPrevYear : function(){
16969         this.update(this.activeDate.add("y", -1));
16970     },
16971
16972     // private
16973     showNextYear : function(){
16974         this.update(this.activeDate.add("y", 1));
16975     },
16976
16977     
16978    // private
16979     update : function(date)
16980     {
16981         var vd = this.activeDate;
16982         this.activeDate = date;
16983 //        if(vd && this.el){
16984 //            var t = date.getTime();
16985 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16986 //                Roo.log('using add remove');
16987 //                
16988 //                this.fireEvent('monthchange', this, date);
16989 //                
16990 //                this.cells.removeClass("fc-state-highlight");
16991 //                this.cells.each(function(c){
16992 //                   if(c.dateValue == t){
16993 //                       c.addClass("fc-state-highlight");
16994 //                       setTimeout(function(){
16995 //                            try{c.dom.firstChild.focus();}catch(e){}
16996 //                       }, 50);
16997 //                       return false;
16998 //                   }
16999 //                   return true;
17000 //                });
17001 //                return;
17002 //            }
17003 //        }
17004         
17005         var days = date.getDaysInMonth();
17006         
17007         var firstOfMonth = date.getFirstDateOfMonth();
17008         var startingPos = firstOfMonth.getDay()-this.startDay;
17009         
17010         if(startingPos < this.startDay){
17011             startingPos += 7;
17012         }
17013         
17014         var pm = date.add(Date.MONTH, -1);
17015         var prevStart = pm.getDaysInMonth()-startingPos;
17016 //        
17017         this.cells = this.el.select('.fc-day',true);
17018         this.textNodes = this.el.query('.fc-day-number');
17019         this.cells.addClassOnOver('fc-state-hover');
17020         
17021         var cells = this.cells.elements;
17022         var textEls = this.textNodes;
17023         
17024         Roo.each(cells, function(cell){
17025             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17026         });
17027         
17028         days += startingPos;
17029
17030         // convert everything to numbers so it's fast
17031         var day = 86400000;
17032         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17033         //Roo.log(d);
17034         //Roo.log(pm);
17035         //Roo.log(prevStart);
17036         
17037         var today = new Date().clearTime().getTime();
17038         var sel = date.clearTime().getTime();
17039         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17040         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17041         var ddMatch = this.disabledDatesRE;
17042         var ddText = this.disabledDatesText;
17043         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17044         var ddaysText = this.disabledDaysText;
17045         var format = this.format;
17046         
17047         var setCellClass = function(cal, cell){
17048             cell.row = 0;
17049             cell.events = [];
17050             cell.more = [];
17051             //Roo.log('set Cell Class');
17052             cell.title = "";
17053             var t = d.getTime();
17054             
17055             //Roo.log(d);
17056             
17057             cell.dateValue = t;
17058             if(t == today){
17059                 cell.className += " fc-today";
17060                 cell.className += " fc-state-highlight";
17061                 cell.title = cal.todayText;
17062             }
17063             if(t == sel){
17064                 // disable highlight in other month..
17065                 //cell.className += " fc-state-highlight";
17066                 
17067             }
17068             // disabling
17069             if(t < min) {
17070                 cell.className = " fc-state-disabled";
17071                 cell.title = cal.minText;
17072                 return;
17073             }
17074             if(t > max) {
17075                 cell.className = " fc-state-disabled";
17076                 cell.title = cal.maxText;
17077                 return;
17078             }
17079             if(ddays){
17080                 if(ddays.indexOf(d.getDay()) != -1){
17081                     cell.title = ddaysText;
17082                     cell.className = " fc-state-disabled";
17083                 }
17084             }
17085             if(ddMatch && format){
17086                 var fvalue = d.dateFormat(format);
17087                 if(ddMatch.test(fvalue)){
17088                     cell.title = ddText.replace("%0", fvalue);
17089                     cell.className = " fc-state-disabled";
17090                 }
17091             }
17092             
17093             if (!cell.initialClassName) {
17094                 cell.initialClassName = cell.dom.className;
17095             }
17096             
17097             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17098         };
17099
17100         var i = 0;
17101         
17102         for(; i < startingPos; i++) {
17103             textEls[i].innerHTML = (++prevStart);
17104             d.setDate(d.getDate()+1);
17105             
17106             cells[i].className = "fc-past fc-other-month";
17107             setCellClass(this, cells[i]);
17108         }
17109         
17110         var intDay = 0;
17111         
17112         for(; i < days; i++){
17113             intDay = i - startingPos + 1;
17114             textEls[i].innerHTML = (intDay);
17115             d.setDate(d.getDate()+1);
17116             
17117             cells[i].className = ''; // "x-date-active";
17118             setCellClass(this, cells[i]);
17119         }
17120         var extraDays = 0;
17121         
17122         for(; i < 42; i++) {
17123             textEls[i].innerHTML = (++extraDays);
17124             d.setDate(d.getDate()+1);
17125             
17126             cells[i].className = "fc-future fc-other-month";
17127             setCellClass(this, cells[i]);
17128         }
17129         
17130         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17131         
17132         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17133         
17134         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17135         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17136         
17137         if(totalRows != 6){
17138             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17139             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17140         }
17141         
17142         this.fireEvent('monthchange', this, date);
17143         
17144         
17145         /*
17146         if(!this.internalRender){
17147             var main = this.el.dom.firstChild;
17148             var w = main.offsetWidth;
17149             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17150             Roo.fly(main).setWidth(w);
17151             this.internalRender = true;
17152             // opera does not respect the auto grow header center column
17153             // then, after it gets a width opera refuses to recalculate
17154             // without a second pass
17155             if(Roo.isOpera && !this.secondPass){
17156                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17157                 this.secondPass = true;
17158                 this.update.defer(10, this, [date]);
17159             }
17160         }
17161         */
17162         
17163     },
17164     
17165     findCell : function(dt) {
17166         dt = dt.clearTime().getTime();
17167         var ret = false;
17168         this.cells.each(function(c){
17169             //Roo.log("check " +c.dateValue + '?=' + dt);
17170             if(c.dateValue == dt){
17171                 ret = c;
17172                 return false;
17173             }
17174             return true;
17175         });
17176         
17177         return ret;
17178     },
17179     
17180     findCells : function(ev) {
17181         var s = ev.start.clone().clearTime().getTime();
17182        // Roo.log(s);
17183         var e= ev.end.clone().clearTime().getTime();
17184        // Roo.log(e);
17185         var ret = [];
17186         this.cells.each(function(c){
17187              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17188             
17189             if(c.dateValue > e){
17190                 return ;
17191             }
17192             if(c.dateValue < s){
17193                 return ;
17194             }
17195             ret.push(c);
17196         });
17197         
17198         return ret;    
17199     },
17200     
17201 //    findBestRow: function(cells)
17202 //    {
17203 //        var ret = 0;
17204 //        
17205 //        for (var i =0 ; i < cells.length;i++) {
17206 //            ret  = Math.max(cells[i].rows || 0,ret);
17207 //        }
17208 //        return ret;
17209 //        
17210 //    },
17211     
17212     
17213     addItem : function(ev)
17214     {
17215         // look for vertical location slot in
17216         var cells = this.findCells(ev);
17217         
17218 //        ev.row = this.findBestRow(cells);
17219         
17220         // work out the location.
17221         
17222         var crow = false;
17223         var rows = [];
17224         for(var i =0; i < cells.length; i++) {
17225             
17226             cells[i].row = cells[0].row;
17227             
17228             if(i == 0){
17229                 cells[i].row = cells[i].row + 1;
17230             }
17231             
17232             if (!crow) {
17233                 crow = {
17234                     start : cells[i],
17235                     end :  cells[i]
17236                 };
17237                 continue;
17238             }
17239             if (crow.start.getY() == cells[i].getY()) {
17240                 // on same row.
17241                 crow.end = cells[i];
17242                 continue;
17243             }
17244             // different row.
17245             rows.push(crow);
17246             crow = {
17247                 start: cells[i],
17248                 end : cells[i]
17249             };
17250             
17251         }
17252         
17253         rows.push(crow);
17254         ev.els = [];
17255         ev.rows = rows;
17256         ev.cells = cells;
17257         
17258         cells[0].events.push(ev);
17259         
17260         this.calevents.push(ev);
17261     },
17262     
17263     clearEvents: function() {
17264         
17265         if(!this.calevents){
17266             return;
17267         }
17268         
17269         Roo.each(this.cells.elements, function(c){
17270             c.row = 0;
17271             c.events = [];
17272             c.more = [];
17273         });
17274         
17275         Roo.each(this.calevents, function(e) {
17276             Roo.each(e.els, function(el) {
17277                 el.un('mouseenter' ,this.onEventEnter, this);
17278                 el.un('mouseleave' ,this.onEventLeave, this);
17279                 el.remove();
17280             },this);
17281         },this);
17282         
17283         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17284             e.remove();
17285         });
17286         
17287     },
17288     
17289     renderEvents: function()
17290     {   
17291         var _this = this;
17292         
17293         this.cells.each(function(c) {
17294             
17295             if(c.row < 5){
17296                 return;
17297             }
17298             
17299             var ev = c.events;
17300             
17301             var r = 4;
17302             if(c.row != c.events.length){
17303                 r = 4 - (4 - (c.row - c.events.length));
17304             }
17305             
17306             c.events = ev.slice(0, r);
17307             c.more = ev.slice(r);
17308             
17309             if(c.more.length && c.more.length == 1){
17310                 c.events.push(c.more.pop());
17311             }
17312             
17313             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17314             
17315         });
17316             
17317         this.cells.each(function(c) {
17318             
17319             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17320             
17321             
17322             for (var e = 0; e < c.events.length; e++){
17323                 var ev = c.events[e];
17324                 var rows = ev.rows;
17325                 
17326                 for(var i = 0; i < rows.length; i++) {
17327                 
17328                     // how many rows should it span..
17329
17330                     var  cfg = {
17331                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17332                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17333
17334                         unselectable : "on",
17335                         cn : [
17336                             {
17337                                 cls: 'fc-event-inner',
17338                                 cn : [
17339     //                                {
17340     //                                  tag:'span',
17341     //                                  cls: 'fc-event-time',
17342     //                                  html : cells.length > 1 ? '' : ev.time
17343     //                                },
17344                                     {
17345                                       tag:'span',
17346                                       cls: 'fc-event-title',
17347                                       html : String.format('{0}', ev.title)
17348                                     }
17349
17350
17351                                 ]
17352                             },
17353                             {
17354                                 cls: 'ui-resizable-handle ui-resizable-e',
17355                                 html : '&nbsp;&nbsp;&nbsp'
17356                             }
17357
17358                         ]
17359                     };
17360
17361                     if (i == 0) {
17362                         cfg.cls += ' fc-event-start';
17363                     }
17364                     if ((i+1) == rows.length) {
17365                         cfg.cls += ' fc-event-end';
17366                     }
17367
17368                     var ctr = _this.el.select('.fc-event-container',true).first();
17369                     var cg = ctr.createChild(cfg);
17370
17371                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17372                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17373
17374                     var r = (c.more.length) ? 1 : 0;
17375                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17376                     cg.setWidth(ebox.right - sbox.x -2);
17377
17378                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17379                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17380                     cg.on('click', _this.onEventClick, _this, ev);
17381
17382                     ev.els.push(cg);
17383                     
17384                 }
17385                 
17386             }
17387             
17388             
17389             if(c.more.length){
17390                 var  cfg = {
17391                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17392                     style : 'position: absolute',
17393                     unselectable : "on",
17394                     cn : [
17395                         {
17396                             cls: 'fc-event-inner',
17397                             cn : [
17398                                 {
17399                                   tag:'span',
17400                                   cls: 'fc-event-title',
17401                                   html : 'More'
17402                                 }
17403
17404
17405                             ]
17406                         },
17407                         {
17408                             cls: 'ui-resizable-handle ui-resizable-e',
17409                             html : '&nbsp;&nbsp;&nbsp'
17410                         }
17411
17412                     ]
17413                 };
17414
17415                 var ctr = _this.el.select('.fc-event-container',true).first();
17416                 var cg = ctr.createChild(cfg);
17417
17418                 var sbox = c.select('.fc-day-content',true).first().getBox();
17419                 var ebox = c.select('.fc-day-content',true).first().getBox();
17420                 //Roo.log(cg);
17421                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17422                 cg.setWidth(ebox.right - sbox.x -2);
17423
17424                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17425                 
17426             }
17427             
17428         });
17429         
17430         
17431         
17432     },
17433     
17434     onEventEnter: function (e, el,event,d) {
17435         this.fireEvent('evententer', this, el, event);
17436     },
17437     
17438     onEventLeave: function (e, el,event,d) {
17439         this.fireEvent('eventleave', this, el, event);
17440     },
17441     
17442     onEventClick: function (e, el,event,d) {
17443         this.fireEvent('eventclick', this, el, event);
17444     },
17445     
17446     onMonthChange: function () {
17447         this.store.load();
17448     },
17449     
17450     onMoreEventClick: function(e, el, more)
17451     {
17452         var _this = this;
17453         
17454         this.calpopover.placement = 'right';
17455         this.calpopover.setTitle('More');
17456         
17457         this.calpopover.setContent('');
17458         
17459         var ctr = this.calpopover.el.select('.popover-content', true).first();
17460         
17461         Roo.each(more, function(m){
17462             var cfg = {
17463                 cls : 'fc-event-hori fc-event-draggable',
17464                 html : m.title
17465             };
17466             var cg = ctr.createChild(cfg);
17467             
17468             cg.on('click', _this.onEventClick, _this, m);
17469         });
17470         
17471         this.calpopover.show(el);
17472         
17473         
17474     },
17475     
17476     onLoad: function () 
17477     {   
17478         this.calevents = [];
17479         var cal = this;
17480         
17481         if(this.store.getCount() > 0){
17482             this.store.data.each(function(d){
17483                cal.addItem({
17484                     id : d.data.id,
17485                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17486                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17487                     time : d.data.start_time,
17488                     title : d.data.title,
17489                     description : d.data.description,
17490                     venue : d.data.venue
17491                 });
17492             });
17493         }
17494         
17495         this.renderEvents();
17496         
17497         if(this.calevents.length && this.loadMask){
17498             this.maskEl.hide();
17499         }
17500     },
17501     
17502     onBeforeLoad: function()
17503     {
17504         this.clearEvents();
17505         if(this.loadMask){
17506             this.maskEl.show();
17507         }
17508     }
17509 });
17510
17511  
17512  /*
17513  * - LGPL
17514  *
17515  * element
17516  * 
17517  */
17518
17519 /**
17520  * @class Roo.bootstrap.Popover
17521  * @extends Roo.bootstrap.Component
17522  * Bootstrap Popover class
17523  * @cfg {String} html contents of the popover   (or false to use children..)
17524  * @cfg {String} title of popover (or false to hide)
17525  * @cfg {String} placement how it is placed
17526  * @cfg {String} trigger click || hover (or false to trigger manually)
17527  * @cfg {String} over what (parent or false to trigger manually.)
17528  * @cfg {Number} delay - delay before showing
17529  
17530  * @constructor
17531  * Create a new Popover
17532  * @param {Object} config The config object
17533  */
17534
17535 Roo.bootstrap.Popover = function(config){
17536     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17537     
17538     this.addEvents({
17539         // raw events
17540          /**
17541          * @event show
17542          * After the popover show
17543          * 
17544          * @param {Roo.bootstrap.Popover} this
17545          */
17546         "show" : true,
17547         /**
17548          * @event hide
17549          * After the popover hide
17550          * 
17551          * @param {Roo.bootstrap.Popover} this
17552          */
17553         "hide" : true
17554     });
17555 };
17556
17557 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17558     
17559     title: 'Fill in a title',
17560     html: false,
17561     
17562     placement : 'right',
17563     trigger : 'hover', // hover
17564     
17565     delay : 0,
17566     
17567     over: 'parent',
17568     
17569     can_build_overlaid : false,
17570     
17571     getChildContainer : function()
17572     {
17573         return this.el.select('.popover-content',true).first();
17574     },
17575     
17576     getAutoCreate : function(){
17577          
17578         var cfg = {
17579            cls : 'popover roo-dynamic',
17580            style: 'display:block',
17581            cn : [
17582                 {
17583                     cls : 'arrow'
17584                 },
17585                 {
17586                     cls : 'popover-inner',
17587                     cn : [
17588                         {
17589                             tag: 'h3',
17590                             cls: 'popover-title',
17591                             html : this.title
17592                         },
17593                         {
17594                             cls : 'popover-content',
17595                             html : this.html
17596                         }
17597                     ]
17598                     
17599                 }
17600            ]
17601         };
17602         
17603         return cfg;
17604     },
17605     setTitle: function(str)
17606     {
17607         this.title = str;
17608         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17609     },
17610     setContent: function(str)
17611     {
17612         this.html = str;
17613         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17614     },
17615     // as it get's added to the bottom of the page.
17616     onRender : function(ct, position)
17617     {
17618         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17619         if(!this.el){
17620             var cfg = Roo.apply({},  this.getAutoCreate());
17621             cfg.id = Roo.id();
17622             
17623             if (this.cls) {
17624                 cfg.cls += ' ' + this.cls;
17625             }
17626             if (this.style) {
17627                 cfg.style = this.style;
17628             }
17629             //Roo.log("adding to ");
17630             this.el = Roo.get(document.body).createChild(cfg, position);
17631 //            Roo.log(this.el);
17632         }
17633         this.initEvents();
17634     },
17635     
17636     initEvents : function()
17637     {
17638         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17639         this.el.enableDisplayMode('block');
17640         this.el.hide();
17641         if (this.over === false) {
17642             return; 
17643         }
17644         if (this.triggers === false) {
17645             return;
17646         }
17647         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17648         var triggers = this.trigger ? this.trigger.split(' ') : [];
17649         Roo.each(triggers, function(trigger) {
17650         
17651             if (trigger == 'click') {
17652                 on_el.on('click', this.toggle, this);
17653             } else if (trigger != 'manual') {
17654                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17655                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17656       
17657                 on_el.on(eventIn  ,this.enter, this);
17658                 on_el.on(eventOut, this.leave, this);
17659             }
17660         }, this);
17661         
17662     },
17663     
17664     
17665     // private
17666     timeout : null,
17667     hoverState : null,
17668     
17669     toggle : function () {
17670         this.hoverState == 'in' ? this.leave() : this.enter();
17671     },
17672     
17673     enter : function () {
17674         
17675         clearTimeout(this.timeout);
17676     
17677         this.hoverState = 'in';
17678     
17679         if (!this.delay || !this.delay.show) {
17680             this.show();
17681             return;
17682         }
17683         var _t = this;
17684         this.timeout = setTimeout(function () {
17685             if (_t.hoverState == 'in') {
17686                 _t.show();
17687             }
17688         }, this.delay.show)
17689     },
17690     
17691     leave : function() {
17692         clearTimeout(this.timeout);
17693     
17694         this.hoverState = 'out';
17695     
17696         if (!this.delay || !this.delay.hide) {
17697             this.hide();
17698             return;
17699         }
17700         var _t = this;
17701         this.timeout = setTimeout(function () {
17702             if (_t.hoverState == 'out') {
17703                 _t.hide();
17704             }
17705         }, this.delay.hide)
17706     },
17707     
17708     show : function (on_el)
17709     {
17710         if (!on_el) {
17711             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17712         }
17713         
17714         // set content.
17715         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17716         if (this.html !== false) {
17717             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17718         }
17719         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17720         if (!this.title.length) {
17721             this.el.select('.popover-title',true).hide();
17722         }
17723         
17724         var placement = typeof this.placement == 'function' ?
17725             this.placement.call(this, this.el, on_el) :
17726             this.placement;
17727             
17728         var autoToken = /\s?auto?\s?/i;
17729         var autoPlace = autoToken.test(placement);
17730         if (autoPlace) {
17731             placement = placement.replace(autoToken, '') || 'top';
17732         }
17733         
17734         //this.el.detach()
17735         //this.el.setXY([0,0]);
17736         this.el.show();
17737         this.el.dom.style.display='block';
17738         this.el.addClass(placement);
17739         
17740         //this.el.appendTo(on_el);
17741         
17742         var p = this.getPosition();
17743         var box = this.el.getBox();
17744         
17745         if (autoPlace) {
17746             // fixme..
17747         }
17748         var align = Roo.bootstrap.Popover.alignment[placement];
17749         
17750 //        Roo.log(align);
17751         this.el.alignTo(on_el, align[0],align[1]);
17752         //var arrow = this.el.select('.arrow',true).first();
17753         //arrow.set(align[2], 
17754         
17755         this.el.addClass('in');
17756         
17757         
17758         if (this.el.hasClass('fade')) {
17759             // fade it?
17760         }
17761         
17762         this.hoverState = 'in';
17763         
17764         this.fireEvent('show', this);
17765         
17766     },
17767     hide : function()
17768     {
17769         this.el.setXY([0,0]);
17770         this.el.removeClass('in');
17771         this.el.hide();
17772         this.hoverState = null;
17773         
17774         this.fireEvent('hide', this);
17775     }
17776     
17777 });
17778
17779 Roo.bootstrap.Popover.alignment = {
17780     'left' : ['r-l', [-10,0], 'right'],
17781     'right' : ['l-r', [10,0], 'left'],
17782     'bottom' : ['t-b', [0,10], 'top'],
17783     'top' : [ 'b-t', [0,-10], 'bottom']
17784 };
17785
17786  /*
17787  * - LGPL
17788  *
17789  * Progress
17790  * 
17791  */
17792
17793 /**
17794  * @class Roo.bootstrap.Progress
17795  * @extends Roo.bootstrap.Component
17796  * Bootstrap Progress class
17797  * @cfg {Boolean} striped striped of the progress bar
17798  * @cfg {Boolean} active animated of the progress bar
17799  * 
17800  * 
17801  * @constructor
17802  * Create a new Progress
17803  * @param {Object} config The config object
17804  */
17805
17806 Roo.bootstrap.Progress = function(config){
17807     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17808 };
17809
17810 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17811     
17812     striped : false,
17813     active: false,
17814     
17815     getAutoCreate : function(){
17816         var cfg = {
17817             tag: 'div',
17818             cls: 'progress'
17819         };
17820         
17821         
17822         if(this.striped){
17823             cfg.cls += ' progress-striped';
17824         }
17825       
17826         if(this.active){
17827             cfg.cls += ' active';
17828         }
17829         
17830         
17831         return cfg;
17832     }
17833    
17834 });
17835
17836  
17837
17838  /*
17839  * - LGPL
17840  *
17841  * ProgressBar
17842  * 
17843  */
17844
17845 /**
17846  * @class Roo.bootstrap.ProgressBar
17847  * @extends Roo.bootstrap.Component
17848  * Bootstrap ProgressBar class
17849  * @cfg {Number} aria_valuenow aria-value now
17850  * @cfg {Number} aria_valuemin aria-value min
17851  * @cfg {Number} aria_valuemax aria-value max
17852  * @cfg {String} label label for the progress bar
17853  * @cfg {String} panel (success | info | warning | danger )
17854  * @cfg {String} role role of the progress bar
17855  * @cfg {String} sr_only text
17856  * 
17857  * 
17858  * @constructor
17859  * Create a new ProgressBar
17860  * @param {Object} config The config object
17861  */
17862
17863 Roo.bootstrap.ProgressBar = function(config){
17864     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17865 };
17866
17867 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17868     
17869     aria_valuenow : 0,
17870     aria_valuemin : 0,
17871     aria_valuemax : 100,
17872     label : false,
17873     panel : false,
17874     role : false,
17875     sr_only: false,
17876     
17877     getAutoCreate : function()
17878     {
17879         
17880         var cfg = {
17881             tag: 'div',
17882             cls: 'progress-bar',
17883             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17884         };
17885         
17886         if(this.sr_only){
17887             cfg.cn = {
17888                 tag: 'span',
17889                 cls: 'sr-only',
17890                 html: this.sr_only
17891             }
17892         }
17893         
17894         if(this.role){
17895             cfg.role = this.role;
17896         }
17897         
17898         if(this.aria_valuenow){
17899             cfg['aria-valuenow'] = this.aria_valuenow;
17900         }
17901         
17902         if(this.aria_valuemin){
17903             cfg['aria-valuemin'] = this.aria_valuemin;
17904         }
17905         
17906         if(this.aria_valuemax){
17907             cfg['aria-valuemax'] = this.aria_valuemax;
17908         }
17909         
17910         if(this.label && !this.sr_only){
17911             cfg.html = this.label;
17912         }
17913         
17914         if(this.panel){
17915             cfg.cls += ' progress-bar-' + this.panel;
17916         }
17917         
17918         return cfg;
17919     },
17920     
17921     update : function(aria_valuenow)
17922     {
17923         this.aria_valuenow = aria_valuenow;
17924         
17925         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17926     }
17927    
17928 });
17929
17930  
17931
17932  /*
17933  * - LGPL
17934  *
17935  * column
17936  * 
17937  */
17938
17939 /**
17940  * @class Roo.bootstrap.TabGroup
17941  * @extends Roo.bootstrap.Column
17942  * Bootstrap Column class
17943  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17944  * @cfg {Boolean} carousel true to make the group behave like a carousel
17945  * @cfg {Boolean} bullets show bullets for the panels
17946  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17947  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17948  * @cfg {Boolean} showarrow (true|false) show arrow default true
17949  * 
17950  * @constructor
17951  * Create a new TabGroup
17952  * @param {Object} config The config object
17953  */
17954
17955 Roo.bootstrap.TabGroup = function(config){
17956     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17957     if (!this.navId) {
17958         this.navId = Roo.id();
17959     }
17960     this.tabs = [];
17961     Roo.bootstrap.TabGroup.register(this);
17962     
17963 };
17964
17965 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17966     
17967     carousel : false,
17968     transition : false,
17969     bullets : 0,
17970     timer : 0,
17971     autoslide : false,
17972     slideFn : false,
17973     slideOnTouch : false,
17974     showarrow : true,
17975     
17976     getAutoCreate : function()
17977     {
17978         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17979         
17980         cfg.cls += ' tab-content';
17981         
17982         if (this.carousel) {
17983             cfg.cls += ' carousel slide';
17984             
17985             cfg.cn = [{
17986                cls : 'carousel-inner',
17987                cn : []
17988             }];
17989         
17990             if(this.bullets  && !Roo.isTouch){
17991                 
17992                 var bullets = {
17993                     cls : 'carousel-bullets',
17994                     cn : []
17995                 };
17996                
17997                 if(this.bullets_cls){
17998                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17999                 }
18000                 
18001                 bullets.cn.push({
18002                     cls : 'clear'
18003                 });
18004                 
18005                 cfg.cn[0].cn.push(bullets);
18006             }
18007             
18008             if(this.showarrow){
18009                 cfg.cn[0].cn.push({
18010                     tag : 'div',
18011                     class : 'carousel-arrow',
18012                     cn : [
18013                         {
18014                             tag : 'div',
18015                             class : 'carousel-prev',
18016                             cn : [
18017                                 {
18018                                     tag : 'i',
18019                                     class : 'fa fa-chevron-left'
18020                                 }
18021                             ]
18022                         },
18023                         {
18024                             tag : 'div',
18025                             class : 'carousel-next',
18026                             cn : [
18027                                 {
18028                                     tag : 'i',
18029                                     class : 'fa fa-chevron-right'
18030                                 }
18031                             ]
18032                         }
18033                     ]
18034                 });
18035             }
18036             
18037         }
18038         
18039         return cfg;
18040     },
18041     
18042     initEvents:  function()
18043     {
18044 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18045 //            this.el.on("touchstart", this.onTouchStart, this);
18046 //        }
18047         
18048         if(this.autoslide){
18049             var _this = this;
18050             
18051             this.slideFn = window.setInterval(function() {
18052                 _this.showPanelNext();
18053             }, this.timer);
18054         }
18055         
18056         if(this.showarrow){
18057             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18058             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18059         }
18060         
18061         
18062     },
18063     
18064 //    onTouchStart : function(e, el, o)
18065 //    {
18066 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18067 //            return;
18068 //        }
18069 //        
18070 //        this.showPanelNext();
18071 //    },
18072     
18073     
18074     getChildContainer : function()
18075     {
18076         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18077     },
18078     
18079     /**
18080     * register a Navigation item
18081     * @param {Roo.bootstrap.NavItem} the navitem to add
18082     */
18083     register : function(item)
18084     {
18085         this.tabs.push( item);
18086         item.navId = this.navId; // not really needed..
18087         this.addBullet();
18088     
18089     },
18090     
18091     getActivePanel : function()
18092     {
18093         var r = false;
18094         Roo.each(this.tabs, function(t) {
18095             if (t.active) {
18096                 r = t;
18097                 return false;
18098             }
18099             return null;
18100         });
18101         return r;
18102         
18103     },
18104     getPanelByName : function(n)
18105     {
18106         var r = false;
18107         Roo.each(this.tabs, function(t) {
18108             if (t.tabId == n) {
18109                 r = t;
18110                 return false;
18111             }
18112             return null;
18113         });
18114         return r;
18115     },
18116     indexOfPanel : function(p)
18117     {
18118         var r = false;
18119         Roo.each(this.tabs, function(t,i) {
18120             if (t.tabId == p.tabId) {
18121                 r = i;
18122                 return false;
18123             }
18124             return null;
18125         });
18126         return r;
18127     },
18128     /**
18129      * show a specific panel
18130      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18131      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18132      */
18133     showPanel : function (pan)
18134     {
18135         if(this.transition || typeof(pan) == 'undefined'){
18136             Roo.log("waiting for the transitionend");
18137             return;
18138         }
18139         
18140         if (typeof(pan) == 'number') {
18141             pan = this.tabs[pan];
18142         }
18143         
18144         if (typeof(pan) == 'string') {
18145             pan = this.getPanelByName(pan);
18146         }
18147         
18148         var cur = this.getActivePanel();
18149         
18150         if(!pan || !cur){
18151             Roo.log('pan or acitve pan is undefined');
18152             return false;
18153         }
18154         
18155         if (pan.tabId == this.getActivePanel().tabId) {
18156             return true;
18157         }
18158         
18159         if (false === cur.fireEvent('beforedeactivate')) {
18160             return false;
18161         }
18162         
18163         if(this.bullets > 0 && !Roo.isTouch){
18164             this.setActiveBullet(this.indexOfPanel(pan));
18165         }
18166         
18167         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18168             
18169             this.transition = true;
18170             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18171             var lr = dir == 'next' ? 'left' : 'right';
18172             pan.el.addClass(dir); // or prev
18173             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18174             cur.el.addClass(lr); // or right
18175             pan.el.addClass(lr);
18176             
18177             var _this = this;
18178             cur.el.on('transitionend', function() {
18179                 Roo.log("trans end?");
18180                 
18181                 pan.el.removeClass([lr,dir]);
18182                 pan.setActive(true);
18183                 
18184                 cur.el.removeClass([lr]);
18185                 cur.setActive(false);
18186                 
18187                 _this.transition = false;
18188                 
18189             }, this, { single:  true } );
18190             
18191             return true;
18192         }
18193         
18194         cur.setActive(false);
18195         pan.setActive(true);
18196         
18197         return true;
18198         
18199     },
18200     showPanelNext : function()
18201     {
18202         var i = this.indexOfPanel(this.getActivePanel());
18203         
18204         if (i >= this.tabs.length - 1 && !this.autoslide) {
18205             return;
18206         }
18207         
18208         if (i >= this.tabs.length - 1 && this.autoslide) {
18209             i = -1;
18210         }
18211         
18212         this.showPanel(this.tabs[i+1]);
18213     },
18214     
18215     showPanelPrev : function()
18216     {
18217         var i = this.indexOfPanel(this.getActivePanel());
18218         
18219         if (i  < 1 && !this.autoslide) {
18220             return;
18221         }
18222         
18223         if (i < 1 && this.autoslide) {
18224             i = this.tabs.length;
18225         }
18226         
18227         this.showPanel(this.tabs[i-1]);
18228     },
18229     
18230     
18231     addBullet: function()
18232     {
18233         if(!this.bullets || Roo.isTouch){
18234             return;
18235         }
18236         var ctr = this.el.select('.carousel-bullets',true).first();
18237         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18238         var bullet = ctr.createChild({
18239             cls : 'bullet bullet-' + i
18240         },ctr.dom.lastChild);
18241         
18242         
18243         var _this = this;
18244         
18245         bullet.on('click', (function(e, el, o, ii, t){
18246
18247             e.preventDefault();
18248
18249             this.showPanel(ii);
18250
18251             if(this.autoslide && this.slideFn){
18252                 clearInterval(this.slideFn);
18253                 this.slideFn = window.setInterval(function() {
18254                     _this.showPanelNext();
18255                 }, this.timer);
18256             }
18257
18258         }).createDelegate(this, [i, bullet], true));
18259                 
18260         
18261     },
18262      
18263     setActiveBullet : function(i)
18264     {
18265         if(Roo.isTouch){
18266             return;
18267         }
18268         
18269         Roo.each(this.el.select('.bullet', true).elements, function(el){
18270             el.removeClass('selected');
18271         });
18272
18273         var bullet = this.el.select('.bullet-' + i, true).first();
18274         
18275         if(!bullet){
18276             return;
18277         }
18278         
18279         bullet.addClass('selected');
18280     }
18281     
18282     
18283   
18284 });
18285
18286  
18287
18288  
18289  
18290 Roo.apply(Roo.bootstrap.TabGroup, {
18291     
18292     groups: {},
18293      /**
18294     * register a Navigation Group
18295     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18296     */
18297     register : function(navgrp)
18298     {
18299         this.groups[navgrp.navId] = navgrp;
18300         
18301     },
18302     /**
18303     * fetch a Navigation Group based on the navigation ID
18304     * if one does not exist , it will get created.
18305     * @param {string} the navgroup to add
18306     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18307     */
18308     get: function(navId) {
18309         if (typeof(this.groups[navId]) == 'undefined') {
18310             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18311         }
18312         return this.groups[navId] ;
18313     }
18314     
18315     
18316     
18317 });
18318
18319  /*
18320  * - LGPL
18321  *
18322  * TabPanel
18323  * 
18324  */
18325
18326 /**
18327  * @class Roo.bootstrap.TabPanel
18328  * @extends Roo.bootstrap.Component
18329  * Bootstrap TabPanel class
18330  * @cfg {Boolean} active panel active
18331  * @cfg {String} html panel content
18332  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18333  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18334  * @cfg {String} href click to link..
18335  * 
18336  * 
18337  * @constructor
18338  * Create a new TabPanel
18339  * @param {Object} config The config object
18340  */
18341
18342 Roo.bootstrap.TabPanel = function(config){
18343     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18344     this.addEvents({
18345         /**
18346              * @event changed
18347              * Fires when the active status changes
18348              * @param {Roo.bootstrap.TabPanel} this
18349              * @param {Boolean} state the new state
18350             
18351          */
18352         'changed': true,
18353         /**
18354              * @event beforedeactivate
18355              * Fires before a tab is de-activated - can be used to do validation on a form.
18356              * @param {Roo.bootstrap.TabPanel} this
18357              * @return {Boolean} false if there is an error
18358             
18359          */
18360         'beforedeactivate': true
18361      });
18362     
18363     this.tabId = this.tabId || Roo.id();
18364   
18365 };
18366
18367 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18368     
18369     active: false,
18370     html: false,
18371     tabId: false,
18372     navId : false,
18373     href : '',
18374     
18375     getAutoCreate : function(){
18376         var cfg = {
18377             tag: 'div',
18378             // item is needed for carousel - not sure if it has any effect otherwise
18379             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18380             html: this.html || ''
18381         };
18382         
18383         if(this.active){
18384             cfg.cls += ' active';
18385         }
18386         
18387         if(this.tabId){
18388             cfg.tabId = this.tabId;
18389         }
18390         
18391         
18392         return cfg;
18393     },
18394     
18395     initEvents:  function()
18396     {
18397         var p = this.parent();
18398         
18399         this.navId = this.navId || p.navId;
18400         
18401         if (typeof(this.navId) != 'undefined') {
18402             // not really needed.. but just in case.. parent should be a NavGroup.
18403             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18404             
18405             tg.register(this);
18406             
18407             var i = tg.tabs.length - 1;
18408             
18409             if(this.active && tg.bullets > 0 && i < tg.bullets){
18410                 tg.setActiveBullet(i);
18411             }
18412         }
18413         
18414         this.el.on('click', this.onClick, this);
18415         
18416         if(Roo.isTouch){
18417             this.el.on("touchstart", this.onTouchStart, this);
18418             this.el.on("touchmove", this.onTouchMove, this);
18419             this.el.on("touchend", this.onTouchEnd, this);
18420         }
18421         
18422     },
18423     
18424     onRender : function(ct, position)
18425     {
18426         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18427     },
18428     
18429     setActive : function(state)
18430     {
18431         Roo.log("panel - set active " + this.tabId + "=" + state);
18432         
18433         this.active = state;
18434         if (!state) {
18435             this.el.removeClass('active');
18436             
18437         } else  if (!this.el.hasClass('active')) {
18438             this.el.addClass('active');
18439         }
18440         
18441         this.fireEvent('changed', this, state);
18442     },
18443     
18444     onClick : function(e)
18445     {
18446         e.preventDefault();
18447         
18448         if(!this.href.length){
18449             return;
18450         }
18451         
18452         window.location.href = this.href;
18453     },
18454     
18455     startX : 0,
18456     startY : 0,
18457     endX : 0,
18458     endY : 0,
18459     swiping : false,
18460     
18461     onTouchStart : function(e)
18462     {
18463         this.swiping = false;
18464         
18465         this.startX = e.browserEvent.touches[0].clientX;
18466         this.startY = e.browserEvent.touches[0].clientY;
18467     },
18468     
18469     onTouchMove : function(e)
18470     {
18471         this.swiping = true;
18472         
18473         this.endX = e.browserEvent.touches[0].clientX;
18474         this.endY = e.browserEvent.touches[0].clientY;
18475     },
18476     
18477     onTouchEnd : function(e)
18478     {
18479         if(!this.swiping){
18480             this.onClick(e);
18481             return;
18482         }
18483         
18484         var tabGroup = this.parent();
18485         
18486         if(this.endX > this.startX){ // swiping right
18487             tabGroup.showPanelPrev();
18488             return;
18489         }
18490         
18491         if(this.startX > this.endX){ // swiping left
18492             tabGroup.showPanelNext();
18493             return;
18494         }
18495     }
18496     
18497     
18498 });
18499  
18500
18501  
18502
18503  /*
18504  * - LGPL
18505  *
18506  * DateField
18507  * 
18508  */
18509
18510 /**
18511  * @class Roo.bootstrap.DateField
18512  * @extends Roo.bootstrap.Input
18513  * Bootstrap DateField class
18514  * @cfg {Number} weekStart default 0
18515  * @cfg {String} viewMode default empty, (months|years)
18516  * @cfg {String} minViewMode default empty, (months|years)
18517  * @cfg {Number} startDate default -Infinity
18518  * @cfg {Number} endDate default Infinity
18519  * @cfg {Boolean} todayHighlight default false
18520  * @cfg {Boolean} todayBtn default false
18521  * @cfg {Boolean} calendarWeeks default false
18522  * @cfg {Object} daysOfWeekDisabled default empty
18523  * @cfg {Boolean} singleMode default false (true | false)
18524  * 
18525  * @cfg {Boolean} keyboardNavigation default true
18526  * @cfg {String} language default en
18527  * 
18528  * @constructor
18529  * Create a new DateField
18530  * @param {Object} config The config object
18531  */
18532
18533 Roo.bootstrap.DateField = function(config){
18534     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18535      this.addEvents({
18536             /**
18537              * @event show
18538              * Fires when this field show.
18539              * @param {Roo.bootstrap.DateField} this
18540              * @param {Mixed} date The date value
18541              */
18542             show : true,
18543             /**
18544              * @event show
18545              * Fires when this field hide.
18546              * @param {Roo.bootstrap.DateField} this
18547              * @param {Mixed} date The date value
18548              */
18549             hide : true,
18550             /**
18551              * @event select
18552              * Fires when select a date.
18553              * @param {Roo.bootstrap.DateField} this
18554              * @param {Mixed} date The date value
18555              */
18556             select : true,
18557             /**
18558              * @event beforeselect
18559              * Fires when before select a date.
18560              * @param {Roo.bootstrap.DateField} this
18561              * @param {Mixed} date The date value
18562              */
18563             beforeselect : true
18564         });
18565 };
18566
18567 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18568     
18569     /**
18570      * @cfg {String} format
18571      * The default date format string which can be overriden for localization support.  The format must be
18572      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18573      */
18574     format : "m/d/y",
18575     /**
18576      * @cfg {String} altFormats
18577      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18578      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18579      */
18580     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18581     
18582     weekStart : 0,
18583     
18584     viewMode : '',
18585     
18586     minViewMode : '',
18587     
18588     todayHighlight : false,
18589     
18590     todayBtn: false,
18591     
18592     language: 'en',
18593     
18594     keyboardNavigation: true,
18595     
18596     calendarWeeks: false,
18597     
18598     startDate: -Infinity,
18599     
18600     endDate: Infinity,
18601     
18602     daysOfWeekDisabled: [],
18603     
18604     _events: [],
18605     
18606     singleMode : false,
18607     
18608     UTCDate: function()
18609     {
18610         return new Date(Date.UTC.apply(Date, arguments));
18611     },
18612     
18613     UTCToday: function()
18614     {
18615         var today = new Date();
18616         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18617     },
18618     
18619     getDate: function() {
18620             var d = this.getUTCDate();
18621             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18622     },
18623     
18624     getUTCDate: function() {
18625             return this.date;
18626     },
18627     
18628     setDate: function(d) {
18629             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18630     },
18631     
18632     setUTCDate: function(d) {
18633             this.date = d;
18634             this.setValue(this.formatDate(this.date));
18635     },
18636         
18637     onRender: function(ct, position)
18638     {
18639         
18640         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18641         
18642         this.language = this.language || 'en';
18643         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18644         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18645         
18646         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18647         this.format = this.format || 'm/d/y';
18648         this.isInline = false;
18649         this.isInput = true;
18650         this.component = this.el.select('.add-on', true).first() || false;
18651         this.component = (this.component && this.component.length === 0) ? false : this.component;
18652         this.hasInput = this.component && this.inputEl().length;
18653         
18654         if (typeof(this.minViewMode === 'string')) {
18655             switch (this.minViewMode) {
18656                 case 'months':
18657                     this.minViewMode = 1;
18658                     break;
18659                 case 'years':
18660                     this.minViewMode = 2;
18661                     break;
18662                 default:
18663                     this.minViewMode = 0;
18664                     break;
18665             }
18666         }
18667         
18668         if (typeof(this.viewMode === 'string')) {
18669             switch (this.viewMode) {
18670                 case 'months':
18671                     this.viewMode = 1;
18672                     break;
18673                 case 'years':
18674                     this.viewMode = 2;
18675                     break;
18676                 default:
18677                     this.viewMode = 0;
18678                     break;
18679             }
18680         }
18681                 
18682         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18683         
18684 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18685         
18686         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18687         
18688         this.picker().on('mousedown', this.onMousedown, this);
18689         this.picker().on('click', this.onClick, this);
18690         
18691         this.picker().addClass('datepicker-dropdown');
18692         
18693         this.startViewMode = this.viewMode;
18694         
18695         if(this.singleMode){
18696             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18697                 v.setVisibilityMode(Roo.Element.DISPLAY);
18698                 v.hide();
18699             });
18700             
18701             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18702                 v.setStyle('width', '189px');
18703             });
18704         }
18705         
18706         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18707             if(!this.calendarWeeks){
18708                 v.remove();
18709                 return;
18710             }
18711             
18712             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18713             v.attr('colspan', function(i, val){
18714                 return parseInt(val) + 1;
18715             });
18716         });
18717                         
18718         
18719         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18720         
18721         this.setStartDate(this.startDate);
18722         this.setEndDate(this.endDate);
18723         
18724         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18725         
18726         this.fillDow();
18727         this.fillMonths();
18728         this.update();
18729         this.showMode();
18730         
18731         if(this.isInline) {
18732             this.showPopup();
18733         }
18734     },
18735     
18736     picker : function()
18737     {
18738         return this.pickerEl;
18739 //        return this.el.select('.datepicker', true).first();
18740     },
18741     
18742     fillDow: function()
18743     {
18744         var dowCnt = this.weekStart;
18745         
18746         var dow = {
18747             tag: 'tr',
18748             cn: [
18749                 
18750             ]
18751         };
18752         
18753         if(this.calendarWeeks){
18754             dow.cn.push({
18755                 tag: 'th',
18756                 cls: 'cw',
18757                 html: '&nbsp;'
18758             })
18759         }
18760         
18761         while (dowCnt < this.weekStart + 7) {
18762             dow.cn.push({
18763                 tag: 'th',
18764                 cls: 'dow',
18765                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18766             });
18767         }
18768         
18769         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18770     },
18771     
18772     fillMonths: function()
18773     {    
18774         var i = 0;
18775         var months = this.picker().select('>.datepicker-months td', true).first();
18776         
18777         months.dom.innerHTML = '';
18778         
18779         while (i < 12) {
18780             var month = {
18781                 tag: 'span',
18782                 cls: 'month',
18783                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18784             };
18785             
18786             months.createChild(month);
18787         }
18788         
18789     },
18790     
18791     update: function()
18792     {
18793         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;
18794         
18795         if (this.date < this.startDate) {
18796             this.viewDate = new Date(this.startDate);
18797         } else if (this.date > this.endDate) {
18798             this.viewDate = new Date(this.endDate);
18799         } else {
18800             this.viewDate = new Date(this.date);
18801         }
18802         
18803         this.fill();
18804     },
18805     
18806     fill: function() 
18807     {
18808         var d = new Date(this.viewDate),
18809                 year = d.getUTCFullYear(),
18810                 month = d.getUTCMonth(),
18811                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18812                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18813                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18814                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18815                 currentDate = this.date && this.date.valueOf(),
18816                 today = this.UTCToday();
18817         
18818         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18819         
18820 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18821         
18822 //        this.picker.select('>tfoot th.today').
18823 //                                              .text(dates[this.language].today)
18824 //                                              .toggle(this.todayBtn !== false);
18825     
18826         this.updateNavArrows();
18827         this.fillMonths();
18828                                                 
18829         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18830         
18831         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18832          
18833         prevMonth.setUTCDate(day);
18834         
18835         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18836         
18837         var nextMonth = new Date(prevMonth);
18838         
18839         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18840         
18841         nextMonth = nextMonth.valueOf();
18842         
18843         var fillMonths = false;
18844         
18845         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18846         
18847         while(prevMonth.valueOf() <= nextMonth) {
18848             var clsName = '';
18849             
18850             if (prevMonth.getUTCDay() === this.weekStart) {
18851                 if(fillMonths){
18852                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18853                 }
18854                     
18855                 fillMonths = {
18856                     tag: 'tr',
18857                     cn: []
18858                 };
18859                 
18860                 if(this.calendarWeeks){
18861                     // ISO 8601: First week contains first thursday.
18862                     // ISO also states week starts on Monday, but we can be more abstract here.
18863                     var
18864                     // Start of current week: based on weekstart/current date
18865                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18866                     // Thursday of this week
18867                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18868                     // First Thursday of year, year from thursday
18869                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18870                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18871                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18872                     
18873                     fillMonths.cn.push({
18874                         tag: 'td',
18875                         cls: 'cw',
18876                         html: calWeek
18877                     });
18878                 }
18879             }
18880             
18881             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18882                 clsName += ' old';
18883             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18884                 clsName += ' new';
18885             }
18886             if (this.todayHighlight &&
18887                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18888                 prevMonth.getUTCMonth() == today.getMonth() &&
18889                 prevMonth.getUTCDate() == today.getDate()) {
18890                 clsName += ' today';
18891             }
18892             
18893             if (currentDate && prevMonth.valueOf() === currentDate) {
18894                 clsName += ' active';
18895             }
18896             
18897             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18898                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18899                     clsName += ' disabled';
18900             }
18901             
18902             fillMonths.cn.push({
18903                 tag: 'td',
18904                 cls: 'day ' + clsName,
18905                 html: prevMonth.getDate()
18906             });
18907             
18908             prevMonth.setDate(prevMonth.getDate()+1);
18909         }
18910           
18911         var currentYear = this.date && this.date.getUTCFullYear();
18912         var currentMonth = this.date && this.date.getUTCMonth();
18913         
18914         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18915         
18916         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18917             v.removeClass('active');
18918             
18919             if(currentYear === year && k === currentMonth){
18920                 v.addClass('active');
18921             }
18922             
18923             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18924                 v.addClass('disabled');
18925             }
18926             
18927         });
18928         
18929         
18930         year = parseInt(year/10, 10) * 10;
18931         
18932         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18933         
18934         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18935         
18936         year -= 1;
18937         for (var i = -1; i < 11; i++) {
18938             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18939                 tag: 'span',
18940                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18941                 html: year
18942             });
18943             
18944             year += 1;
18945         }
18946     },
18947     
18948     showMode: function(dir) 
18949     {
18950         if (dir) {
18951             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18952         }
18953         
18954         Roo.each(this.picker().select('>div',true).elements, function(v){
18955             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18956             v.hide();
18957         });
18958         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18959     },
18960     
18961     place: function()
18962     {
18963         if(this.isInline) {
18964             return;
18965         }
18966         
18967         this.picker().removeClass(['bottom', 'top']);
18968         
18969         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18970             /*
18971              * place to the top of element!
18972              *
18973              */
18974             
18975             this.picker().addClass('top');
18976             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18977             
18978             return;
18979         }
18980         
18981         this.picker().addClass('bottom');
18982         
18983         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18984     },
18985     
18986     parseDate : function(value)
18987     {
18988         if(!value || value instanceof Date){
18989             return value;
18990         }
18991         var v = Date.parseDate(value, this.format);
18992         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18993             v = Date.parseDate(value, 'Y-m-d');
18994         }
18995         if(!v && this.altFormats){
18996             if(!this.altFormatsArray){
18997                 this.altFormatsArray = this.altFormats.split("|");
18998             }
18999             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19000                 v = Date.parseDate(value, this.altFormatsArray[i]);
19001             }
19002         }
19003         return v;
19004     },
19005     
19006     formatDate : function(date, fmt)
19007     {   
19008         return (!date || !(date instanceof Date)) ?
19009         date : date.dateFormat(fmt || this.format);
19010     },
19011     
19012     onFocus : function()
19013     {
19014         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19015         this.showPopup();
19016     },
19017     
19018     onBlur : function()
19019     {
19020         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19021         
19022         var d = this.inputEl().getValue();
19023         
19024         this.setValue(d);
19025                 
19026         this.hidePopup();
19027     },
19028     
19029     showPopup : function()
19030     {
19031         this.picker().show();
19032         this.update();
19033         this.place();
19034         
19035         this.fireEvent('showpopup', this, this.date);
19036     },
19037     
19038     hidePopup : function()
19039     {
19040         if(this.isInline) {
19041             return;
19042         }
19043         this.picker().hide();
19044         this.viewMode = this.startViewMode;
19045         this.showMode();
19046         
19047         this.fireEvent('hidepopup', this, this.date);
19048         
19049     },
19050     
19051     onMousedown: function(e)
19052     {
19053         e.stopPropagation();
19054         e.preventDefault();
19055     },
19056     
19057     keyup: function(e)
19058     {
19059         Roo.bootstrap.DateField.superclass.keyup.call(this);
19060         this.update();
19061     },
19062
19063     setValue: function(v)
19064     {
19065         if(this.fireEvent('beforeselect', this, v) !== false){
19066             var d = new Date(this.parseDate(v) ).clearTime();
19067         
19068             if(isNaN(d.getTime())){
19069                 this.date = this.viewDate = '';
19070                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19071                 return;
19072             }
19073
19074             v = this.formatDate(d);
19075
19076             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19077
19078             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19079
19080             this.update();
19081
19082             this.fireEvent('select', this, this.date);
19083         }
19084     },
19085     
19086     getValue: function()
19087     {
19088         return this.formatDate(this.date);
19089     },
19090     
19091     fireKey: function(e)
19092     {
19093         if (!this.picker().isVisible()){
19094             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19095                 this.showPopup();
19096             }
19097             return;
19098         }
19099         
19100         var dateChanged = false,
19101         dir, day, month,
19102         newDate, newViewDate;
19103         
19104         switch(e.keyCode){
19105             case 27: // escape
19106                 this.hidePopup();
19107                 e.preventDefault();
19108                 break;
19109             case 37: // left
19110             case 39: // right
19111                 if (!this.keyboardNavigation) {
19112                     break;
19113                 }
19114                 dir = e.keyCode == 37 ? -1 : 1;
19115                 
19116                 if (e.ctrlKey){
19117                     newDate = this.moveYear(this.date, dir);
19118                     newViewDate = this.moveYear(this.viewDate, dir);
19119                 } else if (e.shiftKey){
19120                     newDate = this.moveMonth(this.date, dir);
19121                     newViewDate = this.moveMonth(this.viewDate, dir);
19122                 } else {
19123                     newDate = new Date(this.date);
19124                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19125                     newViewDate = new Date(this.viewDate);
19126                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19127                 }
19128                 if (this.dateWithinRange(newDate)){
19129                     this.date = newDate;
19130                     this.viewDate = newViewDate;
19131                     this.setValue(this.formatDate(this.date));
19132 //                    this.update();
19133                     e.preventDefault();
19134                     dateChanged = true;
19135                 }
19136                 break;
19137             case 38: // up
19138             case 40: // down
19139                 if (!this.keyboardNavigation) {
19140                     break;
19141                 }
19142                 dir = e.keyCode == 38 ? -1 : 1;
19143                 if (e.ctrlKey){
19144                     newDate = this.moveYear(this.date, dir);
19145                     newViewDate = this.moveYear(this.viewDate, dir);
19146                 } else if (e.shiftKey){
19147                     newDate = this.moveMonth(this.date, dir);
19148                     newViewDate = this.moveMonth(this.viewDate, dir);
19149                 } else {
19150                     newDate = new Date(this.date);
19151                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19152                     newViewDate = new Date(this.viewDate);
19153                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19154                 }
19155                 if (this.dateWithinRange(newDate)){
19156                     this.date = newDate;
19157                     this.viewDate = newViewDate;
19158                     this.setValue(this.formatDate(this.date));
19159 //                    this.update();
19160                     e.preventDefault();
19161                     dateChanged = true;
19162                 }
19163                 break;
19164             case 13: // enter
19165                 this.setValue(this.formatDate(this.date));
19166                 this.hidePopup();
19167                 e.preventDefault();
19168                 break;
19169             case 9: // tab
19170                 this.setValue(this.formatDate(this.date));
19171                 this.hidePopup();
19172                 break;
19173             case 16: // shift
19174             case 17: // ctrl
19175             case 18: // alt
19176                 break;
19177             default :
19178                 this.hide();
19179                 
19180         }
19181     },
19182     
19183     
19184     onClick: function(e) 
19185     {
19186         e.stopPropagation();
19187         e.preventDefault();
19188         
19189         var target = e.getTarget();
19190         
19191         if(target.nodeName.toLowerCase() === 'i'){
19192             target = Roo.get(target).dom.parentNode;
19193         }
19194         
19195         var nodeName = target.nodeName;
19196         var className = target.className;
19197         var html = target.innerHTML;
19198         //Roo.log(nodeName);
19199         
19200         switch(nodeName.toLowerCase()) {
19201             case 'th':
19202                 switch(className) {
19203                     case 'switch':
19204                         this.showMode(1);
19205                         break;
19206                     case 'prev':
19207                     case 'next':
19208                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19209                         switch(this.viewMode){
19210                                 case 0:
19211                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19212                                         break;
19213                                 case 1:
19214                                 case 2:
19215                                         this.viewDate = this.moveYear(this.viewDate, dir);
19216                                         break;
19217                         }
19218                         this.fill();
19219                         break;
19220                     case 'today':
19221                         var date = new Date();
19222                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19223 //                        this.fill()
19224                         this.setValue(this.formatDate(this.date));
19225                         
19226                         this.hidePopup();
19227                         break;
19228                 }
19229                 break;
19230             case 'span':
19231                 if (className.indexOf('disabled') < 0) {
19232                     this.viewDate.setUTCDate(1);
19233                     if (className.indexOf('month') > -1) {
19234                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19235                     } else {
19236                         var year = parseInt(html, 10) || 0;
19237                         this.viewDate.setUTCFullYear(year);
19238                         
19239                     }
19240                     
19241                     if(this.singleMode){
19242                         this.setValue(this.formatDate(this.viewDate));
19243                         this.hidePopup();
19244                         return;
19245                     }
19246                     
19247                     this.showMode(-1);
19248                     this.fill();
19249                 }
19250                 break;
19251                 
19252             case 'td':
19253                 //Roo.log(className);
19254                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19255                     var day = parseInt(html, 10) || 1;
19256                     var year = this.viewDate.getUTCFullYear(),
19257                         month = this.viewDate.getUTCMonth();
19258
19259                     if (className.indexOf('old') > -1) {
19260                         if(month === 0 ){
19261                             month = 11;
19262                             year -= 1;
19263                         }else{
19264                             month -= 1;
19265                         }
19266                     } else if (className.indexOf('new') > -1) {
19267                         if (month == 11) {
19268                             month = 0;
19269                             year += 1;
19270                         } else {
19271                             month += 1;
19272                         }
19273                     }
19274                     //Roo.log([year,month,day]);
19275                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19276                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19277 //                    this.fill();
19278                     //Roo.log(this.formatDate(this.date));
19279                     this.setValue(this.formatDate(this.date));
19280                     this.hidePopup();
19281                 }
19282                 break;
19283         }
19284     },
19285     
19286     setStartDate: function(startDate)
19287     {
19288         this.startDate = startDate || -Infinity;
19289         if (this.startDate !== -Infinity) {
19290             this.startDate = this.parseDate(this.startDate);
19291         }
19292         this.update();
19293         this.updateNavArrows();
19294     },
19295
19296     setEndDate: function(endDate)
19297     {
19298         this.endDate = endDate || Infinity;
19299         if (this.endDate !== Infinity) {
19300             this.endDate = this.parseDate(this.endDate);
19301         }
19302         this.update();
19303         this.updateNavArrows();
19304     },
19305     
19306     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19307     {
19308         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19309         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19310             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19311         }
19312         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19313             return parseInt(d, 10);
19314         });
19315         this.update();
19316         this.updateNavArrows();
19317     },
19318     
19319     updateNavArrows: function() 
19320     {
19321         if(this.singleMode){
19322             return;
19323         }
19324         
19325         var d = new Date(this.viewDate),
19326         year = d.getUTCFullYear(),
19327         month = d.getUTCMonth();
19328         
19329         Roo.each(this.picker().select('.prev', true).elements, function(v){
19330             v.show();
19331             switch (this.viewMode) {
19332                 case 0:
19333
19334                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19335                         v.hide();
19336                     }
19337                     break;
19338                 case 1:
19339                 case 2:
19340                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19341                         v.hide();
19342                     }
19343                     break;
19344             }
19345         });
19346         
19347         Roo.each(this.picker().select('.next', true).elements, function(v){
19348             v.show();
19349             switch (this.viewMode) {
19350                 case 0:
19351
19352                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19353                         v.hide();
19354                     }
19355                     break;
19356                 case 1:
19357                 case 2:
19358                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19359                         v.hide();
19360                     }
19361                     break;
19362             }
19363         })
19364     },
19365     
19366     moveMonth: function(date, dir)
19367     {
19368         if (!dir) {
19369             return date;
19370         }
19371         var new_date = new Date(date.valueOf()),
19372         day = new_date.getUTCDate(),
19373         month = new_date.getUTCMonth(),
19374         mag = Math.abs(dir),
19375         new_month, test;
19376         dir = dir > 0 ? 1 : -1;
19377         if (mag == 1){
19378             test = dir == -1
19379             // If going back one month, make sure month is not current month
19380             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19381             ? function(){
19382                 return new_date.getUTCMonth() == month;
19383             }
19384             // If going forward one month, make sure month is as expected
19385             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19386             : function(){
19387                 return new_date.getUTCMonth() != new_month;
19388             };
19389             new_month = month + dir;
19390             new_date.setUTCMonth(new_month);
19391             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19392             if (new_month < 0 || new_month > 11) {
19393                 new_month = (new_month + 12) % 12;
19394             }
19395         } else {
19396             // For magnitudes >1, move one month at a time...
19397             for (var i=0; i<mag; i++) {
19398                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19399                 new_date = this.moveMonth(new_date, dir);
19400             }
19401             // ...then reset the day, keeping it in the new month
19402             new_month = new_date.getUTCMonth();
19403             new_date.setUTCDate(day);
19404             test = function(){
19405                 return new_month != new_date.getUTCMonth();
19406             };
19407         }
19408         // Common date-resetting loop -- if date is beyond end of month, make it
19409         // end of month
19410         while (test()){
19411             new_date.setUTCDate(--day);
19412             new_date.setUTCMonth(new_month);
19413         }
19414         return new_date;
19415     },
19416
19417     moveYear: function(date, dir)
19418     {
19419         return this.moveMonth(date, dir*12);
19420     },
19421
19422     dateWithinRange: function(date)
19423     {
19424         return date >= this.startDate && date <= this.endDate;
19425     },
19426
19427     
19428     remove: function() 
19429     {
19430         this.picker().remove();
19431     },
19432     
19433     validateValue : function(value)
19434     {
19435         if(this.getVisibilityEl().hasClass('hidden')){
19436             return true;
19437         }
19438         
19439         if(value.length < 1)  {
19440             if(this.allowBlank){
19441                 return true;
19442             }
19443             return false;
19444         }
19445         
19446         if(value.length < this.minLength){
19447             return false;
19448         }
19449         if(value.length > this.maxLength){
19450             return false;
19451         }
19452         if(this.vtype){
19453             var vt = Roo.form.VTypes;
19454             if(!vt[this.vtype](value, this)){
19455                 return false;
19456             }
19457         }
19458         if(typeof this.validator == "function"){
19459             var msg = this.validator(value);
19460             if(msg !== true){
19461                 return false;
19462             }
19463         }
19464         
19465         if(this.regex && !this.regex.test(value)){
19466             return false;
19467         }
19468         
19469         if(typeof(this.parseDate(value)) == 'undefined'){
19470             return false;
19471         }
19472         
19473         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19474             return false;
19475         }      
19476         
19477         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19478             return false;
19479         } 
19480         
19481         
19482         return true;
19483     },
19484     
19485     reset : function()
19486     {
19487         this.date = this.viewDate = '';
19488         
19489         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19490     }
19491    
19492 });
19493
19494 Roo.apply(Roo.bootstrap.DateField,  {
19495     
19496     head : {
19497         tag: 'thead',
19498         cn: [
19499         {
19500             tag: 'tr',
19501             cn: [
19502             {
19503                 tag: 'th',
19504                 cls: 'prev',
19505                 html: '<i class="fa fa-arrow-left"/>'
19506             },
19507             {
19508                 tag: 'th',
19509                 cls: 'switch',
19510                 colspan: '5'
19511             },
19512             {
19513                 tag: 'th',
19514                 cls: 'next',
19515                 html: '<i class="fa fa-arrow-right"/>'
19516             }
19517
19518             ]
19519         }
19520         ]
19521     },
19522     
19523     content : {
19524         tag: 'tbody',
19525         cn: [
19526         {
19527             tag: 'tr',
19528             cn: [
19529             {
19530                 tag: 'td',
19531                 colspan: '7'
19532             }
19533             ]
19534         }
19535         ]
19536     },
19537     
19538     footer : {
19539         tag: 'tfoot',
19540         cn: [
19541         {
19542             tag: 'tr',
19543             cn: [
19544             {
19545                 tag: 'th',
19546                 colspan: '7',
19547                 cls: 'today'
19548             }
19549                     
19550             ]
19551         }
19552         ]
19553     },
19554     
19555     dates:{
19556         en: {
19557             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19558             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19559             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19560             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19561             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19562             today: "Today"
19563         }
19564     },
19565     
19566     modes: [
19567     {
19568         clsName: 'days',
19569         navFnc: 'Month',
19570         navStep: 1
19571     },
19572     {
19573         clsName: 'months',
19574         navFnc: 'FullYear',
19575         navStep: 1
19576     },
19577     {
19578         clsName: 'years',
19579         navFnc: 'FullYear',
19580         navStep: 10
19581     }]
19582 });
19583
19584 Roo.apply(Roo.bootstrap.DateField,  {
19585   
19586     template : {
19587         tag: 'div',
19588         cls: 'datepicker dropdown-menu roo-dynamic',
19589         cn: [
19590         {
19591             tag: 'div',
19592             cls: 'datepicker-days',
19593             cn: [
19594             {
19595                 tag: 'table',
19596                 cls: 'table-condensed',
19597                 cn:[
19598                 Roo.bootstrap.DateField.head,
19599                 {
19600                     tag: 'tbody'
19601                 },
19602                 Roo.bootstrap.DateField.footer
19603                 ]
19604             }
19605             ]
19606         },
19607         {
19608             tag: 'div',
19609             cls: 'datepicker-months',
19610             cn: [
19611             {
19612                 tag: 'table',
19613                 cls: 'table-condensed',
19614                 cn:[
19615                 Roo.bootstrap.DateField.head,
19616                 Roo.bootstrap.DateField.content,
19617                 Roo.bootstrap.DateField.footer
19618                 ]
19619             }
19620             ]
19621         },
19622         {
19623             tag: 'div',
19624             cls: 'datepicker-years',
19625             cn: [
19626             {
19627                 tag: 'table',
19628                 cls: 'table-condensed',
19629                 cn:[
19630                 Roo.bootstrap.DateField.head,
19631                 Roo.bootstrap.DateField.content,
19632                 Roo.bootstrap.DateField.footer
19633                 ]
19634             }
19635             ]
19636         }
19637         ]
19638     }
19639 });
19640
19641  
19642
19643  /*
19644  * - LGPL
19645  *
19646  * TimeField
19647  * 
19648  */
19649
19650 /**
19651  * @class Roo.bootstrap.TimeField
19652  * @extends Roo.bootstrap.Input
19653  * Bootstrap DateField class
19654  * 
19655  * 
19656  * @constructor
19657  * Create a new TimeField
19658  * @param {Object} config The config object
19659  */
19660
19661 Roo.bootstrap.TimeField = function(config){
19662     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19663     this.addEvents({
19664             /**
19665              * @event show
19666              * Fires when this field show.
19667              * @param {Roo.bootstrap.DateField} thisthis
19668              * @param {Mixed} date The date value
19669              */
19670             show : true,
19671             /**
19672              * @event show
19673              * Fires when this field hide.
19674              * @param {Roo.bootstrap.DateField} this
19675              * @param {Mixed} date The date value
19676              */
19677             hide : true,
19678             /**
19679              * @event select
19680              * Fires when select a date.
19681              * @param {Roo.bootstrap.DateField} this
19682              * @param {Mixed} date The date value
19683              */
19684             select : true
19685         });
19686 };
19687
19688 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19689     
19690     /**
19691      * @cfg {String} format
19692      * The default time format string which can be overriden for localization support.  The format must be
19693      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19694      */
19695     format : "H:i",
19696        
19697     onRender: function(ct, position)
19698     {
19699         
19700         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19701                 
19702         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19703         
19704         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19705         
19706         this.pop = this.picker().select('>.datepicker-time',true).first();
19707         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19708         
19709         this.picker().on('mousedown', this.onMousedown, this);
19710         this.picker().on('click', this.onClick, this);
19711         
19712         this.picker().addClass('datepicker-dropdown');
19713     
19714         this.fillTime();
19715         this.update();
19716             
19717         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19718         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19719         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19720         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19721         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19722         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19723
19724     },
19725     
19726     fireKey: function(e){
19727         if (!this.picker().isVisible()){
19728             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19729                 this.show();
19730             }
19731             return;
19732         }
19733
19734         e.preventDefault();
19735         
19736         switch(e.keyCode){
19737             case 27: // escape
19738                 this.hide();
19739                 break;
19740             case 37: // left
19741             case 39: // right
19742                 this.onTogglePeriod();
19743                 break;
19744             case 38: // up
19745                 this.onIncrementMinutes();
19746                 break;
19747             case 40: // down
19748                 this.onDecrementMinutes();
19749                 break;
19750             case 13: // enter
19751             case 9: // tab
19752                 this.setTime();
19753                 break;
19754         }
19755     },
19756     
19757     onClick: function(e) {
19758         e.stopPropagation();
19759         e.preventDefault();
19760     },
19761     
19762     picker : function()
19763     {
19764         return this.el.select('.datepicker', true).first();
19765     },
19766     
19767     fillTime: function()
19768     {    
19769         var time = this.pop.select('tbody', true).first();
19770         
19771         time.dom.innerHTML = '';
19772         
19773         time.createChild({
19774             tag: 'tr',
19775             cn: [
19776                 {
19777                     tag: 'td',
19778                     cn: [
19779                         {
19780                             tag: 'a',
19781                             href: '#',
19782                             cls: 'btn',
19783                             cn: [
19784                                 {
19785                                     tag: 'span',
19786                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19787                                 }
19788                             ]
19789                         } 
19790                     ]
19791                 },
19792                 {
19793                     tag: 'td',
19794                     cls: 'separator'
19795                 },
19796                 {
19797                     tag: 'td',
19798                     cn: [
19799                         {
19800                             tag: 'a',
19801                             href: '#',
19802                             cls: 'btn',
19803                             cn: [
19804                                 {
19805                                     tag: 'span',
19806                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19807                                 }
19808                             ]
19809                         }
19810                     ]
19811                 },
19812                 {
19813                     tag: 'td',
19814                     cls: 'separator'
19815                 }
19816             ]
19817         });
19818         
19819         time.createChild({
19820             tag: 'tr',
19821             cn: [
19822                 {
19823                     tag: 'td',
19824                     cn: [
19825                         {
19826                             tag: 'span',
19827                             cls: 'timepicker-hour',
19828                             html: '00'
19829                         }  
19830                     ]
19831                 },
19832                 {
19833                     tag: 'td',
19834                     cls: 'separator',
19835                     html: ':'
19836                 },
19837                 {
19838                     tag: 'td',
19839                     cn: [
19840                         {
19841                             tag: 'span',
19842                             cls: 'timepicker-minute',
19843                             html: '00'
19844                         }  
19845                     ]
19846                 },
19847                 {
19848                     tag: 'td',
19849                     cls: 'separator'
19850                 },
19851                 {
19852                     tag: 'td',
19853                     cn: [
19854                         {
19855                             tag: 'button',
19856                             type: 'button',
19857                             cls: 'btn btn-primary period',
19858                             html: 'AM'
19859                             
19860                         }
19861                     ]
19862                 }
19863             ]
19864         });
19865         
19866         time.createChild({
19867             tag: 'tr',
19868             cn: [
19869                 {
19870                     tag: 'td',
19871                     cn: [
19872                         {
19873                             tag: 'a',
19874                             href: '#',
19875                             cls: 'btn',
19876                             cn: [
19877                                 {
19878                                     tag: 'span',
19879                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19880                                 }
19881                             ]
19882                         }
19883                     ]
19884                 },
19885                 {
19886                     tag: 'td',
19887                     cls: 'separator'
19888                 },
19889                 {
19890                     tag: 'td',
19891                     cn: [
19892                         {
19893                             tag: 'a',
19894                             href: '#',
19895                             cls: 'btn',
19896                             cn: [
19897                                 {
19898                                     tag: 'span',
19899                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19900                                 }
19901                             ]
19902                         }
19903                     ]
19904                 },
19905                 {
19906                     tag: 'td',
19907                     cls: 'separator'
19908                 }
19909             ]
19910         });
19911         
19912     },
19913     
19914     update: function()
19915     {
19916         
19917         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19918         
19919         this.fill();
19920     },
19921     
19922     fill: function() 
19923     {
19924         var hours = this.time.getHours();
19925         var minutes = this.time.getMinutes();
19926         var period = 'AM';
19927         
19928         if(hours > 11){
19929             period = 'PM';
19930         }
19931         
19932         if(hours == 0){
19933             hours = 12;
19934         }
19935         
19936         
19937         if(hours > 12){
19938             hours = hours - 12;
19939         }
19940         
19941         if(hours < 10){
19942             hours = '0' + hours;
19943         }
19944         
19945         if(minutes < 10){
19946             minutes = '0' + minutes;
19947         }
19948         
19949         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19950         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19951         this.pop.select('button', true).first().dom.innerHTML = period;
19952         
19953     },
19954     
19955     place: function()
19956     {   
19957         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19958         
19959         var cls = ['bottom'];
19960         
19961         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19962             cls.pop();
19963             cls.push('top');
19964         }
19965         
19966         cls.push('right');
19967         
19968         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19969             cls.pop();
19970             cls.push('left');
19971         }
19972         
19973         this.picker().addClass(cls.join('-'));
19974         
19975         var _this = this;
19976         
19977         Roo.each(cls, function(c){
19978             if(c == 'bottom'){
19979                 _this.picker().setTop(_this.inputEl().getHeight());
19980                 return;
19981             }
19982             if(c == 'top'){
19983                 _this.picker().setTop(0 - _this.picker().getHeight());
19984                 return;
19985             }
19986             
19987             if(c == 'left'){
19988                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19989                 return;
19990             }
19991             if(c == 'right'){
19992                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19993                 return;
19994             }
19995         });
19996         
19997     },
19998   
19999     onFocus : function()
20000     {
20001         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20002         this.show();
20003     },
20004     
20005     onBlur : function()
20006     {
20007         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20008         this.hide();
20009     },
20010     
20011     show : function()
20012     {
20013         this.picker().show();
20014         this.pop.show();
20015         this.update();
20016         this.place();
20017         
20018         this.fireEvent('show', this, this.date);
20019     },
20020     
20021     hide : function()
20022     {
20023         this.picker().hide();
20024         this.pop.hide();
20025         
20026         this.fireEvent('hide', this, this.date);
20027     },
20028     
20029     setTime : function()
20030     {
20031         this.hide();
20032         this.setValue(this.time.format(this.format));
20033         
20034         this.fireEvent('select', this, this.date);
20035         
20036         
20037     },
20038     
20039     onMousedown: function(e){
20040         e.stopPropagation();
20041         e.preventDefault();
20042     },
20043     
20044     onIncrementHours: function()
20045     {
20046         Roo.log('onIncrementHours');
20047         this.time = this.time.add(Date.HOUR, 1);
20048         this.update();
20049         
20050     },
20051     
20052     onDecrementHours: function()
20053     {
20054         Roo.log('onDecrementHours');
20055         this.time = this.time.add(Date.HOUR, -1);
20056         this.update();
20057     },
20058     
20059     onIncrementMinutes: function()
20060     {
20061         Roo.log('onIncrementMinutes');
20062         this.time = this.time.add(Date.MINUTE, 1);
20063         this.update();
20064     },
20065     
20066     onDecrementMinutes: function()
20067     {
20068         Roo.log('onDecrementMinutes');
20069         this.time = this.time.add(Date.MINUTE, -1);
20070         this.update();
20071     },
20072     
20073     onTogglePeriod: function()
20074     {
20075         Roo.log('onTogglePeriod');
20076         this.time = this.time.add(Date.HOUR, 12);
20077         this.update();
20078     }
20079     
20080    
20081 });
20082
20083 Roo.apply(Roo.bootstrap.TimeField,  {
20084     
20085     content : {
20086         tag: 'tbody',
20087         cn: [
20088             {
20089                 tag: 'tr',
20090                 cn: [
20091                 {
20092                     tag: 'td',
20093                     colspan: '7'
20094                 }
20095                 ]
20096             }
20097         ]
20098     },
20099     
20100     footer : {
20101         tag: 'tfoot',
20102         cn: [
20103             {
20104                 tag: 'tr',
20105                 cn: [
20106                 {
20107                     tag: 'th',
20108                     colspan: '7',
20109                     cls: '',
20110                     cn: [
20111                         {
20112                             tag: 'button',
20113                             cls: 'btn btn-info ok',
20114                             html: 'OK'
20115                         }
20116                     ]
20117                 }
20118
20119                 ]
20120             }
20121         ]
20122     }
20123 });
20124
20125 Roo.apply(Roo.bootstrap.TimeField,  {
20126   
20127     template : {
20128         tag: 'div',
20129         cls: 'datepicker dropdown-menu',
20130         cn: [
20131             {
20132                 tag: 'div',
20133                 cls: 'datepicker-time',
20134                 cn: [
20135                 {
20136                     tag: 'table',
20137                     cls: 'table-condensed',
20138                     cn:[
20139                     Roo.bootstrap.TimeField.content,
20140                     Roo.bootstrap.TimeField.footer
20141                     ]
20142                 }
20143                 ]
20144             }
20145         ]
20146     }
20147 });
20148
20149  
20150
20151  /*
20152  * - LGPL
20153  *
20154  * MonthField
20155  * 
20156  */
20157
20158 /**
20159  * @class Roo.bootstrap.MonthField
20160  * @extends Roo.bootstrap.Input
20161  * Bootstrap MonthField class
20162  * 
20163  * @cfg {String} language default en
20164  * 
20165  * @constructor
20166  * Create a new MonthField
20167  * @param {Object} config The config object
20168  */
20169
20170 Roo.bootstrap.MonthField = function(config){
20171     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20172     
20173     this.addEvents({
20174         /**
20175          * @event show
20176          * Fires when this field show.
20177          * @param {Roo.bootstrap.MonthField} this
20178          * @param {Mixed} date The date value
20179          */
20180         show : true,
20181         /**
20182          * @event show
20183          * Fires when this field hide.
20184          * @param {Roo.bootstrap.MonthField} this
20185          * @param {Mixed} date The date value
20186          */
20187         hide : true,
20188         /**
20189          * @event select
20190          * Fires when select a date.
20191          * @param {Roo.bootstrap.MonthField} this
20192          * @param {String} oldvalue The old value
20193          * @param {String} newvalue The new value
20194          */
20195         select : true
20196     });
20197 };
20198
20199 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20200     
20201     onRender: function(ct, position)
20202     {
20203         
20204         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20205         
20206         this.language = this.language || 'en';
20207         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20208         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20209         
20210         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20211         this.isInline = false;
20212         this.isInput = true;
20213         this.component = this.el.select('.add-on', true).first() || false;
20214         this.component = (this.component && this.component.length === 0) ? false : this.component;
20215         this.hasInput = this.component && this.inputEL().length;
20216         
20217         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20218         
20219         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20220         
20221         this.picker().on('mousedown', this.onMousedown, this);
20222         this.picker().on('click', this.onClick, this);
20223         
20224         this.picker().addClass('datepicker-dropdown');
20225         
20226         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20227             v.setStyle('width', '189px');
20228         });
20229         
20230         this.fillMonths();
20231         
20232         this.update();
20233         
20234         if(this.isInline) {
20235             this.show();
20236         }
20237         
20238     },
20239     
20240     setValue: function(v, suppressEvent)
20241     {   
20242         var o = this.getValue();
20243         
20244         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20245         
20246         this.update();
20247
20248         if(suppressEvent !== true){
20249             this.fireEvent('select', this, o, v);
20250         }
20251         
20252     },
20253     
20254     getValue: function()
20255     {
20256         return this.value;
20257     },
20258     
20259     onClick: function(e) 
20260     {
20261         e.stopPropagation();
20262         e.preventDefault();
20263         
20264         var target = e.getTarget();
20265         
20266         if(target.nodeName.toLowerCase() === 'i'){
20267             target = Roo.get(target).dom.parentNode;
20268         }
20269         
20270         var nodeName = target.nodeName;
20271         var className = target.className;
20272         var html = target.innerHTML;
20273         
20274         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20275             return;
20276         }
20277         
20278         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20279         
20280         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20281         
20282         this.hide();
20283                         
20284     },
20285     
20286     picker : function()
20287     {
20288         return this.pickerEl;
20289     },
20290     
20291     fillMonths: function()
20292     {    
20293         var i = 0;
20294         var months = this.picker().select('>.datepicker-months td', true).first();
20295         
20296         months.dom.innerHTML = '';
20297         
20298         while (i < 12) {
20299             var month = {
20300                 tag: 'span',
20301                 cls: 'month',
20302                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20303             };
20304             
20305             months.createChild(month);
20306         }
20307         
20308     },
20309     
20310     update: function()
20311     {
20312         var _this = this;
20313         
20314         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20315             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20316         }
20317         
20318         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20319             e.removeClass('active');
20320             
20321             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20322                 e.addClass('active');
20323             }
20324         })
20325     },
20326     
20327     place: function()
20328     {
20329         if(this.isInline) {
20330             return;
20331         }
20332         
20333         this.picker().removeClass(['bottom', 'top']);
20334         
20335         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20336             /*
20337              * place to the top of element!
20338              *
20339              */
20340             
20341             this.picker().addClass('top');
20342             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20343             
20344             return;
20345         }
20346         
20347         this.picker().addClass('bottom');
20348         
20349         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20350     },
20351     
20352     onFocus : function()
20353     {
20354         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20355         this.show();
20356     },
20357     
20358     onBlur : function()
20359     {
20360         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20361         
20362         var d = this.inputEl().getValue();
20363         
20364         this.setValue(d);
20365                 
20366         this.hide();
20367     },
20368     
20369     show : function()
20370     {
20371         this.picker().show();
20372         this.picker().select('>.datepicker-months', true).first().show();
20373         this.update();
20374         this.place();
20375         
20376         this.fireEvent('show', this, this.date);
20377     },
20378     
20379     hide : function()
20380     {
20381         if(this.isInline) {
20382             return;
20383         }
20384         this.picker().hide();
20385         this.fireEvent('hide', this, this.date);
20386         
20387     },
20388     
20389     onMousedown: function(e)
20390     {
20391         e.stopPropagation();
20392         e.preventDefault();
20393     },
20394     
20395     keyup: function(e)
20396     {
20397         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20398         this.update();
20399     },
20400
20401     fireKey: function(e)
20402     {
20403         if (!this.picker().isVisible()){
20404             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20405                 this.show();
20406             }
20407             return;
20408         }
20409         
20410         var dir;
20411         
20412         switch(e.keyCode){
20413             case 27: // escape
20414                 this.hide();
20415                 e.preventDefault();
20416                 break;
20417             case 37: // left
20418             case 39: // right
20419                 dir = e.keyCode == 37 ? -1 : 1;
20420                 
20421                 this.vIndex = this.vIndex + dir;
20422                 
20423                 if(this.vIndex < 0){
20424                     this.vIndex = 0;
20425                 }
20426                 
20427                 if(this.vIndex > 11){
20428                     this.vIndex = 11;
20429                 }
20430                 
20431                 if(isNaN(this.vIndex)){
20432                     this.vIndex = 0;
20433                 }
20434                 
20435                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20436                 
20437                 break;
20438             case 38: // up
20439             case 40: // down
20440                 
20441                 dir = e.keyCode == 38 ? -1 : 1;
20442                 
20443                 this.vIndex = this.vIndex + dir * 4;
20444                 
20445                 if(this.vIndex < 0){
20446                     this.vIndex = 0;
20447                 }
20448                 
20449                 if(this.vIndex > 11){
20450                     this.vIndex = 11;
20451                 }
20452                 
20453                 if(isNaN(this.vIndex)){
20454                     this.vIndex = 0;
20455                 }
20456                 
20457                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20458                 break;
20459                 
20460             case 13: // enter
20461                 
20462                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20463                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20464                 }
20465                 
20466                 this.hide();
20467                 e.preventDefault();
20468                 break;
20469             case 9: // tab
20470                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20471                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20472                 }
20473                 this.hide();
20474                 break;
20475             case 16: // shift
20476             case 17: // ctrl
20477             case 18: // alt
20478                 break;
20479             default :
20480                 this.hide();
20481                 
20482         }
20483     },
20484     
20485     remove: function() 
20486     {
20487         this.picker().remove();
20488     }
20489    
20490 });
20491
20492 Roo.apply(Roo.bootstrap.MonthField,  {
20493     
20494     content : {
20495         tag: 'tbody',
20496         cn: [
20497         {
20498             tag: 'tr',
20499             cn: [
20500             {
20501                 tag: 'td',
20502                 colspan: '7'
20503             }
20504             ]
20505         }
20506         ]
20507     },
20508     
20509     dates:{
20510         en: {
20511             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20512             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20513         }
20514     }
20515 });
20516
20517 Roo.apply(Roo.bootstrap.MonthField,  {
20518   
20519     template : {
20520         tag: 'div',
20521         cls: 'datepicker dropdown-menu roo-dynamic',
20522         cn: [
20523             {
20524                 tag: 'div',
20525                 cls: 'datepicker-months',
20526                 cn: [
20527                 {
20528                     tag: 'table',
20529                     cls: 'table-condensed',
20530                     cn:[
20531                         Roo.bootstrap.DateField.content
20532                     ]
20533                 }
20534                 ]
20535             }
20536         ]
20537     }
20538 });
20539
20540  
20541
20542  
20543  /*
20544  * - LGPL
20545  *
20546  * CheckBox
20547  * 
20548  */
20549
20550 /**
20551  * @class Roo.bootstrap.CheckBox
20552  * @extends Roo.bootstrap.Input
20553  * Bootstrap CheckBox class
20554  * 
20555  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20556  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20557  * @cfg {String} boxLabel The text that appears beside the checkbox
20558  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20559  * @cfg {Boolean} checked initnal the element
20560  * @cfg {Boolean} inline inline the element (default false)
20561  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20562  * @cfg {String} tooltip label tooltip
20563  * 
20564  * @constructor
20565  * Create a new CheckBox
20566  * @param {Object} config The config object
20567  */
20568
20569 Roo.bootstrap.CheckBox = function(config){
20570     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20571    
20572     this.addEvents({
20573         /**
20574         * @event check
20575         * Fires when the element is checked or unchecked.
20576         * @param {Roo.bootstrap.CheckBox} this This input
20577         * @param {Boolean} checked The new checked value
20578         */
20579        check : true,
20580        /**
20581         * @event click
20582         * Fires when the element is click.
20583         * @param {Roo.bootstrap.CheckBox} this This input
20584         */
20585        click : true
20586     });
20587     
20588 };
20589
20590 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20591   
20592     inputType: 'checkbox',
20593     inputValue: 1,
20594     valueOff: 0,
20595     boxLabel: false,
20596     checked: false,
20597     weight : false,
20598     inline: false,
20599     tooltip : '',
20600     
20601     getAutoCreate : function()
20602     {
20603         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20604         
20605         var id = Roo.id();
20606         
20607         var cfg = {};
20608         
20609         cfg.cls = 'form-group ' + this.inputType; //input-group
20610         
20611         if(this.inline){
20612             cfg.cls += ' ' + this.inputType + '-inline';
20613         }
20614         
20615         var input =  {
20616             tag: 'input',
20617             id : id,
20618             type : this.inputType,
20619             value : this.inputValue,
20620             cls : 'roo-' + this.inputType, //'form-box',
20621             placeholder : this.placeholder || ''
20622             
20623         };
20624         
20625         if(this.inputType != 'radio'){
20626             var hidden =  {
20627                 tag: 'input',
20628                 type : 'hidden',
20629                 cls : 'roo-hidden-value',
20630                 value : this.checked ? this.inputValue : this.valueOff
20631             };
20632         }
20633         
20634             
20635         if (this.weight) { // Validity check?
20636             cfg.cls += " " + this.inputType + "-" + this.weight;
20637         }
20638         
20639         if (this.disabled) {
20640             input.disabled=true;
20641         }
20642         
20643         if(this.checked){
20644             input.checked = this.checked;
20645         }
20646         
20647         if (this.name) {
20648             
20649             input.name = this.name;
20650             
20651             if(this.inputType != 'radio'){
20652                 hidden.name = this.name;
20653                 input.name = '_hidden_' + this.name;
20654             }
20655         }
20656         
20657         if (this.size) {
20658             input.cls += ' input-' + this.size;
20659         }
20660         
20661         var settings=this;
20662         
20663         ['xs','sm','md','lg'].map(function(size){
20664             if (settings[size]) {
20665                 cfg.cls += ' col-' + size + '-' + settings[size];
20666             }
20667         });
20668         
20669         var inputblock = input;
20670          
20671         if (this.before || this.after) {
20672             
20673             inputblock = {
20674                 cls : 'input-group',
20675                 cn :  [] 
20676             };
20677             
20678             if (this.before) {
20679                 inputblock.cn.push({
20680                     tag :'span',
20681                     cls : 'input-group-addon',
20682                     html : this.before
20683                 });
20684             }
20685             
20686             inputblock.cn.push(input);
20687             
20688             if(this.inputType != 'radio'){
20689                 inputblock.cn.push(hidden);
20690             }
20691             
20692             if (this.after) {
20693                 inputblock.cn.push({
20694                     tag :'span',
20695                     cls : 'input-group-addon',
20696                     html : this.after
20697                 });
20698             }
20699             
20700         }
20701         
20702         if (align ==='left' && this.fieldLabel.length) {
20703 //                Roo.log("left and has label");
20704             cfg.cn = [
20705                 {
20706                     tag: 'label',
20707                     'for' :  id,
20708                     cls : 'control-label',
20709                     html : this.fieldLabel
20710                 },
20711                 {
20712                     cls : "", 
20713                     cn: [
20714                         inputblock
20715                     ]
20716                 }
20717             ];
20718             
20719             if(this.labelWidth > 12){
20720                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20721             }
20722             
20723             if(this.labelWidth < 13 && this.labelmd == 0){
20724                 this.labelmd = this.labelWidth;
20725             }
20726             
20727             if(this.labellg > 0){
20728                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20729                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20730             }
20731             
20732             if(this.labelmd > 0){
20733                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20734                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20735             }
20736             
20737             if(this.labelsm > 0){
20738                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20739                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20740             }
20741             
20742             if(this.labelxs > 0){
20743                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20744                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20745             }
20746             
20747         } else if ( this.fieldLabel.length) {
20748 //                Roo.log(" label");
20749                 cfg.cn = [
20750                    
20751                     {
20752                         tag: this.boxLabel ? 'span' : 'label',
20753                         'for': id,
20754                         cls: 'control-label box-input-label',
20755                         //cls : 'input-group-addon',
20756                         html : this.fieldLabel
20757                     },
20758                     
20759                     inputblock
20760                     
20761                 ];
20762
20763         } else {
20764             
20765 //                Roo.log(" no label && no align");
20766                 cfg.cn = [  inputblock ] ;
20767                 
20768                 
20769         }
20770         
20771         if(this.boxLabel){
20772              var boxLabelCfg = {
20773                 tag: 'label',
20774                 //'for': id, // box label is handled by onclick - so no for...
20775                 cls: 'box-label',
20776                 html: this.boxLabel
20777             };
20778             
20779             if(this.tooltip){
20780                 boxLabelCfg.tooltip = this.tooltip;
20781             }
20782              
20783             cfg.cn.push(boxLabelCfg);
20784         }
20785         
20786         if(this.inputType != 'radio'){
20787             cfg.cn.push(hidden);
20788         }
20789         
20790         return cfg;
20791         
20792     },
20793     
20794     /**
20795      * return the real input element.
20796      */
20797     inputEl: function ()
20798     {
20799         return this.el.select('input.roo-' + this.inputType,true).first();
20800     },
20801     hiddenEl: function ()
20802     {
20803         return this.el.select('input.roo-hidden-value',true).first();
20804     },
20805     
20806     labelEl: function()
20807     {
20808         return this.el.select('label.control-label',true).first();
20809     },
20810     /* depricated... */
20811     
20812     label: function()
20813     {
20814         return this.labelEl();
20815     },
20816     
20817     boxLabelEl: function()
20818     {
20819         return this.el.select('label.box-label',true).first();
20820     },
20821     
20822     initEvents : function()
20823     {
20824 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20825         
20826         this.inputEl().on('click', this.onClick,  this);
20827         
20828         if (this.boxLabel) { 
20829             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20830         }
20831         
20832         this.startValue = this.getValue();
20833         
20834         if(this.groupId){
20835             Roo.bootstrap.CheckBox.register(this);
20836         }
20837     },
20838     
20839     onClick : function(e)
20840     {   
20841         if(this.fireEvent('click', this, e) !== false){
20842             this.setChecked(!this.checked);
20843         }
20844         
20845     },
20846     
20847     setChecked : function(state,suppressEvent)
20848     {
20849         this.startValue = this.getValue();
20850
20851         if(this.inputType == 'radio'){
20852             
20853             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20854                 e.dom.checked = false;
20855             });
20856             
20857             this.inputEl().dom.checked = true;
20858             
20859             this.inputEl().dom.value = this.inputValue;
20860             
20861             if(suppressEvent !== true){
20862                 this.fireEvent('check', this, true);
20863             }
20864             
20865             this.validate();
20866             
20867             return;
20868         }
20869         
20870         this.checked = state;
20871         
20872         this.inputEl().dom.checked = state;
20873         
20874         
20875         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20876         
20877         if(suppressEvent !== true){
20878             this.fireEvent('check', this, state);
20879         }
20880         
20881         this.validate();
20882     },
20883     
20884     getValue : function()
20885     {
20886         if(this.inputType == 'radio'){
20887             return this.getGroupValue();
20888         }
20889         
20890         return this.hiddenEl().dom.value;
20891         
20892     },
20893     
20894     getGroupValue : function()
20895     {
20896         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20897             return '';
20898         }
20899         
20900         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20901     },
20902     
20903     setValue : function(v,suppressEvent)
20904     {
20905         if(this.inputType == 'radio'){
20906             this.setGroupValue(v, suppressEvent);
20907             return;
20908         }
20909         
20910         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20911         
20912         this.validate();
20913     },
20914     
20915     setGroupValue : function(v, suppressEvent)
20916     {
20917         this.startValue = this.getValue();
20918         
20919         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20920             e.dom.checked = false;
20921             
20922             if(e.dom.value == v){
20923                 e.dom.checked = true;
20924             }
20925         });
20926         
20927         if(suppressEvent !== true){
20928             this.fireEvent('check', this, true);
20929         }
20930
20931         this.validate();
20932         
20933         return;
20934     },
20935     
20936     validate : function()
20937     {
20938         if(this.getVisibilityEl().hasClass('hidden')){
20939             return true;
20940         }
20941         
20942         if(
20943                 this.disabled || 
20944                 (this.inputType == 'radio' && this.validateRadio()) ||
20945                 (this.inputType == 'checkbox' && this.validateCheckbox())
20946         ){
20947             this.markValid();
20948             return true;
20949         }
20950         
20951         this.markInvalid();
20952         return false;
20953     },
20954     
20955     validateRadio : function()
20956     {
20957         if(this.getVisibilityEl().hasClass('hidden')){
20958             return true;
20959         }
20960         
20961         if(this.allowBlank){
20962             return true;
20963         }
20964         
20965         var valid = false;
20966         
20967         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20968             if(!e.dom.checked){
20969                 return;
20970             }
20971             
20972             valid = true;
20973             
20974             return false;
20975         });
20976         
20977         return valid;
20978     },
20979     
20980     validateCheckbox : function()
20981     {
20982         if(!this.groupId){
20983             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20984             //return (this.getValue() == this.inputValue) ? true : false;
20985         }
20986         
20987         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20988         
20989         if(!group){
20990             return false;
20991         }
20992         
20993         var r = false;
20994         
20995         for(var i in group){
20996             if(group[i].el.isVisible(true)){
20997                 r = false;
20998                 break;
20999             }
21000             
21001             r = true;
21002         }
21003         
21004         for(var i in group){
21005             if(r){
21006                 break;
21007             }
21008             
21009             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21010         }
21011         
21012         return r;
21013     },
21014     
21015     /**
21016      * Mark this field as valid
21017      */
21018     markValid : function()
21019     {
21020         var _this = this;
21021         
21022         this.fireEvent('valid', this);
21023         
21024         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21025         
21026         if(this.groupId){
21027             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21028         }
21029         
21030         if(label){
21031             label.markValid();
21032         }
21033
21034         if(this.inputType == 'radio'){
21035             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21036                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21037                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21038             });
21039             
21040             return;
21041         }
21042
21043         if(!this.groupId){
21044             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21045             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21046             return;
21047         }
21048         
21049         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21050         
21051         if(!group){
21052             return;
21053         }
21054         
21055         for(var i in group){
21056             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21057             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21058         }
21059     },
21060     
21061      /**
21062      * Mark this field as invalid
21063      * @param {String} msg The validation message
21064      */
21065     markInvalid : function(msg)
21066     {
21067         if(this.allowBlank){
21068             return;
21069         }
21070         
21071         var _this = this;
21072         
21073         this.fireEvent('invalid', this, msg);
21074         
21075         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21076         
21077         if(this.groupId){
21078             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21079         }
21080         
21081         if(label){
21082             label.markInvalid();
21083         }
21084             
21085         if(this.inputType == 'radio'){
21086             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21088                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21089             });
21090             
21091             return;
21092         }
21093         
21094         if(!this.groupId){
21095             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21096             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21097             return;
21098         }
21099         
21100         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21101         
21102         if(!group){
21103             return;
21104         }
21105         
21106         for(var i in group){
21107             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21108             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21109         }
21110         
21111     },
21112     
21113     clearInvalid : function()
21114     {
21115         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21116         
21117         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21118         
21119         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21120         
21121         if (label && label.iconEl) {
21122             label.iconEl.removeClass(label.validClass);
21123             label.iconEl.removeClass(label.invalidClass);
21124         }
21125     },
21126     
21127     disable : function()
21128     {
21129         if(this.inputType != 'radio'){
21130             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21131             return;
21132         }
21133         
21134         var _this = this;
21135         
21136         if(this.rendered){
21137             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21138                 _this.getActionEl().addClass(this.disabledClass);
21139                 e.dom.disabled = true;
21140             });
21141         }
21142         
21143         this.disabled = true;
21144         this.fireEvent("disable", this);
21145         return this;
21146     },
21147
21148     enable : function()
21149     {
21150         if(this.inputType != 'radio'){
21151             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21152             return;
21153         }
21154         
21155         var _this = this;
21156         
21157         if(this.rendered){
21158             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21159                 _this.getActionEl().removeClass(this.disabledClass);
21160                 e.dom.disabled = false;
21161             });
21162         }
21163         
21164         this.disabled = false;
21165         this.fireEvent("enable", this);
21166         return this;
21167     },
21168     
21169     setBoxLabel : function(v)
21170     {
21171         this.boxLabel = v;
21172         
21173         if(this.rendered){
21174             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21175         }
21176     }
21177
21178 });
21179
21180 Roo.apply(Roo.bootstrap.CheckBox, {
21181     
21182     groups: {},
21183     
21184      /**
21185     * register a CheckBox Group
21186     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21187     */
21188     register : function(checkbox)
21189     {
21190         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21191             this.groups[checkbox.groupId] = {};
21192         }
21193         
21194         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21195             return;
21196         }
21197         
21198         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21199         
21200     },
21201     /**
21202     * fetch a CheckBox Group based on the group ID
21203     * @param {string} the group ID
21204     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21205     */
21206     get: function(groupId) {
21207         if (typeof(this.groups[groupId]) == 'undefined') {
21208             return false;
21209         }
21210         
21211         return this.groups[groupId] ;
21212     }
21213     
21214     
21215 });
21216 /*
21217  * - LGPL
21218  *
21219  * RadioItem
21220  * 
21221  */
21222
21223 /**
21224  * @class Roo.bootstrap.Radio
21225  * @extends Roo.bootstrap.Component
21226  * Bootstrap Radio class
21227  * @cfg {String} boxLabel - the label associated
21228  * @cfg {String} value - the value of radio
21229  * 
21230  * @constructor
21231  * Create a new Radio
21232  * @param {Object} config The config object
21233  */
21234 Roo.bootstrap.Radio = function(config){
21235     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21236     
21237 };
21238
21239 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21240     
21241     boxLabel : '',
21242     
21243     value : '',
21244     
21245     getAutoCreate : function()
21246     {
21247         var cfg = {
21248             tag : 'div',
21249             cls : 'form-group radio',
21250             cn : [
21251                 {
21252                     tag : 'label',
21253                     cls : 'box-label',
21254                     html : this.boxLabel
21255                 }
21256             ]
21257         };
21258         
21259         return cfg;
21260     },
21261     
21262     initEvents : function() 
21263     {
21264         this.parent().register(this);
21265         
21266         this.el.on('click', this.onClick, this);
21267         
21268     },
21269     
21270     onClick : function(e)
21271     {
21272         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21273             this.setChecked(true);
21274         }
21275     },
21276     
21277     setChecked : function(state, suppressEvent)
21278     {
21279         this.parent().setValue(this.value, suppressEvent);
21280         
21281     },
21282     
21283     setBoxLabel : function(v)
21284     {
21285         this.boxLabel = v;
21286         
21287         if(this.rendered){
21288             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21289         }
21290     }
21291     
21292 });
21293  
21294
21295  /*
21296  * - LGPL
21297  *
21298  * Input
21299  * 
21300  */
21301
21302 /**
21303  * @class Roo.bootstrap.SecurePass
21304  * @extends Roo.bootstrap.Input
21305  * Bootstrap SecurePass class
21306  *
21307  * 
21308  * @constructor
21309  * Create a new SecurePass
21310  * @param {Object} config The config object
21311  */
21312  
21313 Roo.bootstrap.SecurePass = function (config) {
21314     // these go here, so the translation tool can replace them..
21315     this.errors = {
21316         PwdEmpty: "Please type a password, and then retype it to confirm.",
21317         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21318         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21319         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21320         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21321         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21322         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21323         TooWeak: "Your password is Too Weak."
21324     },
21325     this.meterLabel = "Password strength:";
21326     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21327     this.meterClass = [
21328         "roo-password-meter-tooweak", 
21329         "roo-password-meter-weak", 
21330         "roo-password-meter-medium", 
21331         "roo-password-meter-strong", 
21332         "roo-password-meter-grey"
21333     ];
21334     
21335     this.errors = {};
21336     
21337     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21338 }
21339
21340 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21341     /**
21342      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21343      * {
21344      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21345      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21346      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21347      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21348      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21349      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21350      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21351      * })
21352      */
21353     // private
21354     
21355     meterWidth: 300,
21356     errorMsg :'',    
21357     errors: false,
21358     imageRoot: '/',
21359     /**
21360      * @cfg {String/Object} Label for the strength meter (defaults to
21361      * 'Password strength:')
21362      */
21363     // private
21364     meterLabel: '',
21365     /**
21366      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21367      * ['Weak', 'Medium', 'Strong'])
21368      */
21369     // private    
21370     pwdStrengths: false,    
21371     // private
21372     strength: 0,
21373     // private
21374     _lastPwd: null,
21375     // private
21376     kCapitalLetter: 0,
21377     kSmallLetter: 1,
21378     kDigit: 2,
21379     kPunctuation: 3,
21380     
21381     insecure: false,
21382     // private
21383     initEvents: function ()
21384     {
21385         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21386
21387         if (this.el.is('input[type=password]') && Roo.isSafari) {
21388             this.el.on('keydown', this.SafariOnKeyDown, this);
21389         }
21390
21391         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21392     },
21393     // private
21394     onRender: function (ct, position)
21395     {
21396         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21397         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21398         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21399
21400         this.trigger.createChild({
21401                    cn: [
21402                     {
21403                     //id: 'PwdMeter',
21404                     tag: 'div',
21405                     cls: 'roo-password-meter-grey col-xs-12',
21406                     style: {
21407                         //width: 0,
21408                         //width: this.meterWidth + 'px'                                                
21409                         }
21410                     },
21411                     {                            
21412                          cls: 'roo-password-meter-text'                          
21413                     }
21414                 ]            
21415         });
21416
21417          
21418         if (this.hideTrigger) {
21419             this.trigger.setDisplayed(false);
21420         }
21421         this.setSize(this.width || '', this.height || '');
21422     },
21423     // private
21424     onDestroy: function ()
21425     {
21426         if (this.trigger) {
21427             this.trigger.removeAllListeners();
21428             this.trigger.remove();
21429         }
21430         if (this.wrap) {
21431             this.wrap.remove();
21432         }
21433         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21434     },
21435     // private
21436     checkStrength: function ()
21437     {
21438         var pwd = this.inputEl().getValue();
21439         if (pwd == this._lastPwd) {
21440             return;
21441         }
21442
21443         var strength;
21444         if (this.ClientSideStrongPassword(pwd)) {
21445             strength = 3;
21446         } else if (this.ClientSideMediumPassword(pwd)) {
21447             strength = 2;
21448         } else if (this.ClientSideWeakPassword(pwd)) {
21449             strength = 1;
21450         } else {
21451             strength = 0;
21452         }
21453         
21454         Roo.log('strength1: ' + strength);
21455         
21456         //var pm = this.trigger.child('div/div/div').dom;
21457         var pm = this.trigger.child('div/div');
21458         pm.removeClass(this.meterClass);
21459         pm.addClass(this.meterClass[strength]);
21460                 
21461         
21462         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21463                 
21464         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21465         
21466         this._lastPwd = pwd;
21467     },
21468     reset: function ()
21469     {
21470         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21471         
21472         this._lastPwd = '';
21473         
21474         var pm = this.trigger.child('div/div');
21475         pm.removeClass(this.meterClass);
21476         pm.addClass('roo-password-meter-grey');        
21477         
21478         
21479         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21480         
21481         pt.innerHTML = '';
21482         this.inputEl().dom.type='password';
21483     },
21484     // private
21485     validateValue: function (value)
21486     {
21487         
21488         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21489             return false;
21490         }
21491         if (value.length == 0) {
21492             if (this.allowBlank) {
21493                 this.clearInvalid();
21494                 return true;
21495             }
21496
21497             this.markInvalid(this.errors.PwdEmpty);
21498             this.errorMsg = this.errors.PwdEmpty;
21499             return false;
21500         }
21501         
21502         if(this.insecure){
21503             return true;
21504         }
21505         
21506         if ('[\x21-\x7e]*'.match(value)) {
21507             this.markInvalid(this.errors.PwdBadChar);
21508             this.errorMsg = this.errors.PwdBadChar;
21509             return false;
21510         }
21511         if (value.length < 6) {
21512             this.markInvalid(this.errors.PwdShort);
21513             this.errorMsg = this.errors.PwdShort;
21514             return false;
21515         }
21516         if (value.length > 16) {
21517             this.markInvalid(this.errors.PwdLong);
21518             this.errorMsg = this.errors.PwdLong;
21519             return false;
21520         }
21521         var strength;
21522         if (this.ClientSideStrongPassword(value)) {
21523             strength = 3;
21524         } else if (this.ClientSideMediumPassword(value)) {
21525             strength = 2;
21526         } else if (this.ClientSideWeakPassword(value)) {
21527             strength = 1;
21528         } else {
21529             strength = 0;
21530         }
21531
21532         
21533         if (strength < 2) {
21534             //this.markInvalid(this.errors.TooWeak);
21535             this.errorMsg = this.errors.TooWeak;
21536             //return false;
21537         }
21538         
21539         
21540         console.log('strength2: ' + strength);
21541         
21542         //var pm = this.trigger.child('div/div/div').dom;
21543         
21544         var pm = this.trigger.child('div/div');
21545         pm.removeClass(this.meterClass);
21546         pm.addClass(this.meterClass[strength]);
21547                 
21548         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21549                 
21550         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21551         
21552         this.errorMsg = ''; 
21553         return true;
21554     },
21555     // private
21556     CharacterSetChecks: function (type)
21557     {
21558         this.type = type;
21559         this.fResult = false;
21560     },
21561     // private
21562     isctype: function (character, type)
21563     {
21564         switch (type) {  
21565             case this.kCapitalLetter:
21566                 if (character >= 'A' && character <= 'Z') {
21567                     return true;
21568                 }
21569                 break;
21570             
21571             case this.kSmallLetter:
21572                 if (character >= 'a' && character <= 'z') {
21573                     return true;
21574                 }
21575                 break;
21576             
21577             case this.kDigit:
21578                 if (character >= '0' && character <= '9') {
21579                     return true;
21580                 }
21581                 break;
21582             
21583             case this.kPunctuation:
21584                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21585                     return true;
21586                 }
21587                 break;
21588             
21589             default:
21590                 return false;
21591         }
21592
21593     },
21594     // private
21595     IsLongEnough: function (pwd, size)
21596     {
21597         return !(pwd == null || isNaN(size) || pwd.length < size);
21598     },
21599     // private
21600     SpansEnoughCharacterSets: function (word, nb)
21601     {
21602         if (!this.IsLongEnough(word, nb))
21603         {
21604             return false;
21605         }
21606
21607         var characterSetChecks = new Array(
21608             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21609             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21610         );
21611         
21612         for (var index = 0; index < word.length; ++index) {
21613             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21614                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21615                     characterSetChecks[nCharSet].fResult = true;
21616                     break;
21617                 }
21618             }
21619         }
21620
21621         var nCharSets = 0;
21622         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21623             if (characterSetChecks[nCharSet].fResult) {
21624                 ++nCharSets;
21625             }
21626         }
21627
21628         if (nCharSets < nb) {
21629             return false;
21630         }
21631         return true;
21632     },
21633     // private
21634     ClientSideStrongPassword: function (pwd)
21635     {
21636         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21637     },
21638     // private
21639     ClientSideMediumPassword: function (pwd)
21640     {
21641         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21642     },
21643     // private
21644     ClientSideWeakPassword: function (pwd)
21645     {
21646         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21647     }
21648           
21649 })//<script type="text/javascript">
21650
21651 /*
21652  * Based  Ext JS Library 1.1.1
21653  * Copyright(c) 2006-2007, Ext JS, LLC.
21654  * LGPL
21655  *
21656  */
21657  
21658 /**
21659  * @class Roo.HtmlEditorCore
21660  * @extends Roo.Component
21661  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21662  *
21663  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21664  */
21665
21666 Roo.HtmlEditorCore = function(config){
21667     
21668     
21669     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21670     
21671     
21672     this.addEvents({
21673         /**
21674          * @event initialize
21675          * Fires when the editor is fully initialized (including the iframe)
21676          * @param {Roo.HtmlEditorCore} this
21677          */
21678         initialize: true,
21679         /**
21680          * @event activate
21681          * Fires when the editor is first receives the focus. Any insertion must wait
21682          * until after this event.
21683          * @param {Roo.HtmlEditorCore} this
21684          */
21685         activate: true,
21686          /**
21687          * @event beforesync
21688          * Fires before the textarea is updated with content from the editor iframe. Return false
21689          * to cancel the sync.
21690          * @param {Roo.HtmlEditorCore} this
21691          * @param {String} html
21692          */
21693         beforesync: true,
21694          /**
21695          * @event beforepush
21696          * Fires before the iframe editor is updated with content from the textarea. Return false
21697          * to cancel the push.
21698          * @param {Roo.HtmlEditorCore} this
21699          * @param {String} html
21700          */
21701         beforepush: true,
21702          /**
21703          * @event sync
21704          * Fires when the textarea is updated with content from the editor iframe.
21705          * @param {Roo.HtmlEditorCore} this
21706          * @param {String} html
21707          */
21708         sync: true,
21709          /**
21710          * @event push
21711          * Fires when the iframe editor is updated with content from the textarea.
21712          * @param {Roo.HtmlEditorCore} this
21713          * @param {String} html
21714          */
21715         push: true,
21716         
21717         /**
21718          * @event editorevent
21719          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21720          * @param {Roo.HtmlEditorCore} this
21721          */
21722         editorevent: true
21723         
21724     });
21725     
21726     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21727     
21728     // defaults : white / black...
21729     this.applyBlacklists();
21730     
21731     
21732     
21733 };
21734
21735
21736 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21737
21738
21739      /**
21740      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21741      */
21742     
21743     owner : false,
21744     
21745      /**
21746      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21747      *                        Roo.resizable.
21748      */
21749     resizable : false,
21750      /**
21751      * @cfg {Number} height (in pixels)
21752      */   
21753     height: 300,
21754    /**
21755      * @cfg {Number} width (in pixels)
21756      */   
21757     width: 500,
21758     
21759     /**
21760      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21761      * 
21762      */
21763     stylesheets: false,
21764     
21765     // id of frame..
21766     frameId: false,
21767     
21768     // private properties
21769     validationEvent : false,
21770     deferHeight: true,
21771     initialized : false,
21772     activated : false,
21773     sourceEditMode : false,
21774     onFocus : Roo.emptyFn,
21775     iframePad:3,
21776     hideMode:'offsets',
21777     
21778     clearUp: true,
21779     
21780     // blacklist + whitelisted elements..
21781     black: false,
21782     white: false,
21783      
21784     bodyCls : '',
21785
21786     /**
21787      * Protected method that will not generally be called directly. It
21788      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21789      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21790      */
21791     getDocMarkup : function(){
21792         // body styles..
21793         var st = '';
21794         
21795         // inherit styels from page...?? 
21796         if (this.stylesheets === false) {
21797             
21798             Roo.get(document.head).select('style').each(function(node) {
21799                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21800             });
21801             
21802             Roo.get(document.head).select('link').each(function(node) { 
21803                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21804             });
21805             
21806         } else if (!this.stylesheets.length) {
21807                 // simple..
21808                 st = '<style type="text/css">' +
21809                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21810                    '</style>';
21811         } else { 
21812             st = '<style type="text/css">' +
21813                     this.stylesheets +
21814                 '</style>';
21815         }
21816         
21817         st +=  '<style type="text/css">' +
21818             'IMG { cursor: pointer } ' +
21819         '</style>';
21820
21821         var cls = 'roo-htmleditor-body';
21822         
21823         if(this.bodyCls.length){
21824             cls += ' ' + this.bodyCls;
21825         }
21826         
21827         return '<html><head>' + st  +
21828             //<style type="text/css">' +
21829             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21830             //'</style>' +
21831             ' </head><body class="' +  cls + '"></body></html>';
21832     },
21833
21834     // private
21835     onRender : function(ct, position)
21836     {
21837         var _t = this;
21838         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21839         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21840         
21841         
21842         this.el.dom.style.border = '0 none';
21843         this.el.dom.setAttribute('tabIndex', -1);
21844         this.el.addClass('x-hidden hide');
21845         
21846         
21847         
21848         if(Roo.isIE){ // fix IE 1px bogus margin
21849             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21850         }
21851        
21852         
21853         this.frameId = Roo.id();
21854         
21855          
21856         
21857         var iframe = this.owner.wrap.createChild({
21858             tag: 'iframe',
21859             cls: 'form-control', // bootstrap..
21860             id: this.frameId,
21861             name: this.frameId,
21862             frameBorder : 'no',
21863             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21864         }, this.el
21865         );
21866         
21867         
21868         this.iframe = iframe.dom;
21869
21870          this.assignDocWin();
21871         
21872         this.doc.designMode = 'on';
21873        
21874         this.doc.open();
21875         this.doc.write(this.getDocMarkup());
21876         this.doc.close();
21877
21878         
21879         var task = { // must defer to wait for browser to be ready
21880             run : function(){
21881                 //console.log("run task?" + this.doc.readyState);
21882                 this.assignDocWin();
21883                 if(this.doc.body || this.doc.readyState == 'complete'){
21884                     try {
21885                         this.doc.designMode="on";
21886                     } catch (e) {
21887                         return;
21888                     }
21889                     Roo.TaskMgr.stop(task);
21890                     this.initEditor.defer(10, this);
21891                 }
21892             },
21893             interval : 10,
21894             duration: 10000,
21895             scope: this
21896         };
21897         Roo.TaskMgr.start(task);
21898
21899     },
21900
21901     // private
21902     onResize : function(w, h)
21903     {
21904          Roo.log('resize: ' +w + ',' + h );
21905         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21906         if(!this.iframe){
21907             return;
21908         }
21909         if(typeof w == 'number'){
21910             
21911             this.iframe.style.width = w + 'px';
21912         }
21913         if(typeof h == 'number'){
21914             
21915             this.iframe.style.height = h + 'px';
21916             if(this.doc){
21917                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21918             }
21919         }
21920         
21921     },
21922
21923     /**
21924      * Toggles the editor between standard and source edit mode.
21925      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21926      */
21927     toggleSourceEdit : function(sourceEditMode){
21928         
21929         this.sourceEditMode = sourceEditMode === true;
21930         
21931         if(this.sourceEditMode){
21932  
21933             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21934             
21935         }else{
21936             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21937             //this.iframe.className = '';
21938             this.deferFocus();
21939         }
21940         //this.setSize(this.owner.wrap.getSize());
21941         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21942     },
21943
21944     
21945   
21946
21947     /**
21948      * Protected method that will not generally be called directly. If you need/want
21949      * custom HTML cleanup, this is the method you should override.
21950      * @param {String} html The HTML to be cleaned
21951      * return {String} The cleaned HTML
21952      */
21953     cleanHtml : function(html){
21954         html = String(html);
21955         if(html.length > 5){
21956             if(Roo.isSafari){ // strip safari nonsense
21957                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21958             }
21959         }
21960         if(html == '&nbsp;'){
21961             html = '';
21962         }
21963         return html;
21964     },
21965
21966     /**
21967      * HTML Editor -> Textarea
21968      * Protected method that will not generally be called directly. Syncs the contents
21969      * of the editor iframe with the textarea.
21970      */
21971     syncValue : function(){
21972         if(this.initialized){
21973             var bd = (this.doc.body || this.doc.documentElement);
21974             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21975             var html = bd.innerHTML;
21976             if(Roo.isSafari){
21977                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21978                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21979                 if(m && m[1]){
21980                     html = '<div style="'+m[0]+'">' + html + '</div>';
21981                 }
21982             }
21983             html = this.cleanHtml(html);
21984             // fix up the special chars.. normaly like back quotes in word...
21985             // however we do not want to do this with chinese..
21986             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21987                 var cc = b.charCodeAt();
21988                 if (
21989                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21990                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21991                     (cc >= 0xf900 && cc < 0xfb00 )
21992                 ) {
21993                         return b;
21994                 }
21995                 return "&#"+cc+";" 
21996             });
21997             if(this.owner.fireEvent('beforesync', this, html) !== false){
21998                 this.el.dom.value = html;
21999                 this.owner.fireEvent('sync', this, html);
22000             }
22001         }
22002     },
22003
22004     /**
22005      * Protected method that will not generally be called directly. Pushes the value of the textarea
22006      * into the iframe editor.
22007      */
22008     pushValue : function(){
22009         if(this.initialized){
22010             var v = this.el.dom.value.trim();
22011             
22012 //            if(v.length < 1){
22013 //                v = '&#160;';
22014 //            }
22015             
22016             if(this.owner.fireEvent('beforepush', this, v) !== false){
22017                 var d = (this.doc.body || this.doc.documentElement);
22018                 d.innerHTML = v;
22019                 this.cleanUpPaste();
22020                 this.el.dom.value = d.innerHTML;
22021                 this.owner.fireEvent('push', this, v);
22022             }
22023         }
22024     },
22025
22026     // private
22027     deferFocus : function(){
22028         this.focus.defer(10, this);
22029     },
22030
22031     // doc'ed in Field
22032     focus : function(){
22033         if(this.win && !this.sourceEditMode){
22034             this.win.focus();
22035         }else{
22036             this.el.focus();
22037         }
22038     },
22039     
22040     assignDocWin: function()
22041     {
22042         var iframe = this.iframe;
22043         
22044          if(Roo.isIE){
22045             this.doc = iframe.contentWindow.document;
22046             this.win = iframe.contentWindow;
22047         } else {
22048 //            if (!Roo.get(this.frameId)) {
22049 //                return;
22050 //            }
22051 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22052 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22053             
22054             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22055                 return;
22056             }
22057             
22058             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22059             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22060         }
22061     },
22062     
22063     // private
22064     initEditor : function(){
22065         //console.log("INIT EDITOR");
22066         this.assignDocWin();
22067         
22068         
22069         
22070         this.doc.designMode="on";
22071         this.doc.open();
22072         this.doc.write(this.getDocMarkup());
22073         this.doc.close();
22074         
22075         var dbody = (this.doc.body || this.doc.documentElement);
22076         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22077         // this copies styles from the containing element into thsi one..
22078         // not sure why we need all of this..
22079         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22080         
22081         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22082         //ss['background-attachment'] = 'fixed'; // w3c
22083         dbody.bgProperties = 'fixed'; // ie
22084         //Roo.DomHelper.applyStyles(dbody, ss);
22085         Roo.EventManager.on(this.doc, {
22086             //'mousedown': this.onEditorEvent,
22087             'mouseup': this.onEditorEvent,
22088             'dblclick': this.onEditorEvent,
22089             'click': this.onEditorEvent,
22090             'keyup': this.onEditorEvent,
22091             buffer:100,
22092             scope: this
22093         });
22094         if(Roo.isGecko){
22095             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22096         }
22097         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22098             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22099         }
22100         this.initialized = true;
22101
22102         this.owner.fireEvent('initialize', this);
22103         this.pushValue();
22104     },
22105
22106     // private
22107     onDestroy : function(){
22108         
22109         
22110         
22111         if(this.rendered){
22112             
22113             //for (var i =0; i < this.toolbars.length;i++) {
22114             //    // fixme - ask toolbars for heights?
22115             //    this.toolbars[i].onDestroy();
22116            // }
22117             
22118             //this.wrap.dom.innerHTML = '';
22119             //this.wrap.remove();
22120         }
22121     },
22122
22123     // private
22124     onFirstFocus : function(){
22125         
22126         this.assignDocWin();
22127         
22128         
22129         this.activated = true;
22130          
22131     
22132         if(Roo.isGecko){ // prevent silly gecko errors
22133             this.win.focus();
22134             var s = this.win.getSelection();
22135             if(!s.focusNode || s.focusNode.nodeType != 3){
22136                 var r = s.getRangeAt(0);
22137                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22138                 r.collapse(true);
22139                 this.deferFocus();
22140             }
22141             try{
22142                 this.execCmd('useCSS', true);
22143                 this.execCmd('styleWithCSS', false);
22144             }catch(e){}
22145         }
22146         this.owner.fireEvent('activate', this);
22147     },
22148
22149     // private
22150     adjustFont: function(btn){
22151         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22152         //if(Roo.isSafari){ // safari
22153         //    adjust *= 2;
22154        // }
22155         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22156         if(Roo.isSafari){ // safari
22157             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22158             v =  (v < 10) ? 10 : v;
22159             v =  (v > 48) ? 48 : v;
22160             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22161             
22162         }
22163         
22164         
22165         v = Math.max(1, v+adjust);
22166         
22167         this.execCmd('FontSize', v  );
22168     },
22169
22170     onEditorEvent : function(e)
22171     {
22172         this.owner.fireEvent('editorevent', this, e);
22173       //  this.updateToolbar();
22174         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22175     },
22176
22177     insertTag : function(tg)
22178     {
22179         // could be a bit smarter... -> wrap the current selected tRoo..
22180         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22181             
22182             range = this.createRange(this.getSelection());
22183             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22184             wrappingNode.appendChild(range.extractContents());
22185             range.insertNode(wrappingNode);
22186
22187             return;
22188             
22189             
22190             
22191         }
22192         this.execCmd("formatblock",   tg);
22193         
22194     },
22195     
22196     insertText : function(txt)
22197     {
22198         
22199         
22200         var range = this.createRange();
22201         range.deleteContents();
22202                //alert(Sender.getAttribute('label'));
22203                
22204         range.insertNode(this.doc.createTextNode(txt));
22205     } ,
22206     
22207      
22208
22209     /**
22210      * Executes a Midas editor command on the editor document and performs necessary focus and
22211      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22212      * @param {String} cmd The Midas command
22213      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22214      */
22215     relayCmd : function(cmd, value){
22216         this.win.focus();
22217         this.execCmd(cmd, value);
22218         this.owner.fireEvent('editorevent', this);
22219         //this.updateToolbar();
22220         this.owner.deferFocus();
22221     },
22222
22223     /**
22224      * Executes a Midas editor command directly on the editor document.
22225      * For visual commands, you should use {@link #relayCmd} instead.
22226      * <b>This should only be called after the editor is initialized.</b>
22227      * @param {String} cmd The Midas command
22228      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22229      */
22230     execCmd : function(cmd, value){
22231         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22232         this.syncValue();
22233     },
22234  
22235  
22236    
22237     /**
22238      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22239      * to insert tRoo.
22240      * @param {String} text | dom node.. 
22241      */
22242     insertAtCursor : function(text)
22243     {
22244         
22245         if(!this.activated){
22246             return;
22247         }
22248         /*
22249         if(Roo.isIE){
22250             this.win.focus();
22251             var r = this.doc.selection.createRange();
22252             if(r){
22253                 r.collapse(true);
22254                 r.pasteHTML(text);
22255                 this.syncValue();
22256                 this.deferFocus();
22257             
22258             }
22259             return;
22260         }
22261         */
22262         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22263             this.win.focus();
22264             
22265             
22266             // from jquery ui (MIT licenced)
22267             var range, node;
22268             var win = this.win;
22269             
22270             if (win.getSelection && win.getSelection().getRangeAt) {
22271                 range = win.getSelection().getRangeAt(0);
22272                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22273                 range.insertNode(node);
22274             } else if (win.document.selection && win.document.selection.createRange) {
22275                 // no firefox support
22276                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22277                 win.document.selection.createRange().pasteHTML(txt);
22278             } else {
22279                 // no firefox support
22280                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22281                 this.execCmd('InsertHTML', txt);
22282             } 
22283             
22284             this.syncValue();
22285             
22286             this.deferFocus();
22287         }
22288     },
22289  // private
22290     mozKeyPress : function(e){
22291         if(e.ctrlKey){
22292             var c = e.getCharCode(), cmd;
22293           
22294             if(c > 0){
22295                 c = String.fromCharCode(c).toLowerCase();
22296                 switch(c){
22297                     case 'b':
22298                         cmd = 'bold';
22299                         break;
22300                     case 'i':
22301                         cmd = 'italic';
22302                         break;
22303                     
22304                     case 'u':
22305                         cmd = 'underline';
22306                         break;
22307                     
22308                     case 'v':
22309                         this.cleanUpPaste.defer(100, this);
22310                         return;
22311                         
22312                 }
22313                 if(cmd){
22314                     this.win.focus();
22315                     this.execCmd(cmd);
22316                     this.deferFocus();
22317                     e.preventDefault();
22318                 }
22319                 
22320             }
22321         }
22322     },
22323
22324     // private
22325     fixKeys : function(){ // load time branching for fastest keydown performance
22326         if(Roo.isIE){
22327             return function(e){
22328                 var k = e.getKey(), r;
22329                 if(k == e.TAB){
22330                     e.stopEvent();
22331                     r = this.doc.selection.createRange();
22332                     if(r){
22333                         r.collapse(true);
22334                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22335                         this.deferFocus();
22336                     }
22337                     return;
22338                 }
22339                 
22340                 if(k == e.ENTER){
22341                     r = this.doc.selection.createRange();
22342                     if(r){
22343                         var target = r.parentElement();
22344                         if(!target || target.tagName.toLowerCase() != 'li'){
22345                             e.stopEvent();
22346                             r.pasteHTML('<br />');
22347                             r.collapse(false);
22348                             r.select();
22349                         }
22350                     }
22351                 }
22352                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22353                     this.cleanUpPaste.defer(100, this);
22354                     return;
22355                 }
22356                 
22357                 
22358             };
22359         }else if(Roo.isOpera){
22360             return function(e){
22361                 var k = e.getKey();
22362                 if(k == e.TAB){
22363                     e.stopEvent();
22364                     this.win.focus();
22365                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22366                     this.deferFocus();
22367                 }
22368                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22369                     this.cleanUpPaste.defer(100, this);
22370                     return;
22371                 }
22372                 
22373             };
22374         }else if(Roo.isSafari){
22375             return function(e){
22376                 var k = e.getKey();
22377                 
22378                 if(k == e.TAB){
22379                     e.stopEvent();
22380                     this.execCmd('InsertText','\t');
22381                     this.deferFocus();
22382                     return;
22383                 }
22384                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22385                     this.cleanUpPaste.defer(100, this);
22386                     return;
22387                 }
22388                 
22389              };
22390         }
22391     }(),
22392     
22393     getAllAncestors: function()
22394     {
22395         var p = this.getSelectedNode();
22396         var a = [];
22397         if (!p) {
22398             a.push(p); // push blank onto stack..
22399             p = this.getParentElement();
22400         }
22401         
22402         
22403         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22404             a.push(p);
22405             p = p.parentNode;
22406         }
22407         a.push(this.doc.body);
22408         return a;
22409     },
22410     lastSel : false,
22411     lastSelNode : false,
22412     
22413     
22414     getSelection : function() 
22415     {
22416         this.assignDocWin();
22417         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22418     },
22419     
22420     getSelectedNode: function() 
22421     {
22422         // this may only work on Gecko!!!
22423         
22424         // should we cache this!!!!
22425         
22426         
22427         
22428          
22429         var range = this.createRange(this.getSelection()).cloneRange();
22430         
22431         if (Roo.isIE) {
22432             var parent = range.parentElement();
22433             while (true) {
22434                 var testRange = range.duplicate();
22435                 testRange.moveToElementText(parent);
22436                 if (testRange.inRange(range)) {
22437                     break;
22438                 }
22439                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22440                     break;
22441                 }
22442                 parent = parent.parentElement;
22443             }
22444             return parent;
22445         }
22446         
22447         // is ancestor a text element.
22448         var ac =  range.commonAncestorContainer;
22449         if (ac.nodeType == 3) {
22450             ac = ac.parentNode;
22451         }
22452         
22453         var ar = ac.childNodes;
22454          
22455         var nodes = [];
22456         var other_nodes = [];
22457         var has_other_nodes = false;
22458         for (var i=0;i<ar.length;i++) {
22459             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22460                 continue;
22461             }
22462             // fullly contained node.
22463             
22464             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22465                 nodes.push(ar[i]);
22466                 continue;
22467             }
22468             
22469             // probably selected..
22470             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22471                 other_nodes.push(ar[i]);
22472                 continue;
22473             }
22474             // outer..
22475             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22476                 continue;
22477             }
22478             
22479             
22480             has_other_nodes = true;
22481         }
22482         if (!nodes.length && other_nodes.length) {
22483             nodes= other_nodes;
22484         }
22485         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22486             return false;
22487         }
22488         
22489         return nodes[0];
22490     },
22491     createRange: function(sel)
22492     {
22493         // this has strange effects when using with 
22494         // top toolbar - not sure if it's a great idea.
22495         //this.editor.contentWindow.focus();
22496         if (typeof sel != "undefined") {
22497             try {
22498                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22499             } catch(e) {
22500                 return this.doc.createRange();
22501             }
22502         } else {
22503             return this.doc.createRange();
22504         }
22505     },
22506     getParentElement: function()
22507     {
22508         
22509         this.assignDocWin();
22510         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22511         
22512         var range = this.createRange(sel);
22513          
22514         try {
22515             var p = range.commonAncestorContainer;
22516             while (p.nodeType == 3) { // text node
22517                 p = p.parentNode;
22518             }
22519             return p;
22520         } catch (e) {
22521             return null;
22522         }
22523     
22524     },
22525     /***
22526      *
22527      * Range intersection.. the hard stuff...
22528      *  '-1' = before
22529      *  '0' = hits..
22530      *  '1' = after.
22531      *         [ -- selected range --- ]
22532      *   [fail]                        [fail]
22533      *
22534      *    basically..
22535      *      if end is before start or  hits it. fail.
22536      *      if start is after end or hits it fail.
22537      *
22538      *   if either hits (but other is outside. - then it's not 
22539      *   
22540      *    
22541      **/
22542     
22543     
22544     // @see http://www.thismuchiknow.co.uk/?p=64.
22545     rangeIntersectsNode : function(range, node)
22546     {
22547         var nodeRange = node.ownerDocument.createRange();
22548         try {
22549             nodeRange.selectNode(node);
22550         } catch (e) {
22551             nodeRange.selectNodeContents(node);
22552         }
22553     
22554         var rangeStartRange = range.cloneRange();
22555         rangeStartRange.collapse(true);
22556     
22557         var rangeEndRange = range.cloneRange();
22558         rangeEndRange.collapse(false);
22559     
22560         var nodeStartRange = nodeRange.cloneRange();
22561         nodeStartRange.collapse(true);
22562     
22563         var nodeEndRange = nodeRange.cloneRange();
22564         nodeEndRange.collapse(false);
22565     
22566         return rangeStartRange.compareBoundaryPoints(
22567                  Range.START_TO_START, nodeEndRange) == -1 &&
22568                rangeEndRange.compareBoundaryPoints(
22569                  Range.START_TO_START, nodeStartRange) == 1;
22570         
22571          
22572     },
22573     rangeCompareNode : function(range, node)
22574     {
22575         var nodeRange = node.ownerDocument.createRange();
22576         try {
22577             nodeRange.selectNode(node);
22578         } catch (e) {
22579             nodeRange.selectNodeContents(node);
22580         }
22581         
22582         
22583         range.collapse(true);
22584     
22585         nodeRange.collapse(true);
22586      
22587         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22588         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22589          
22590         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22591         
22592         var nodeIsBefore   =  ss == 1;
22593         var nodeIsAfter    = ee == -1;
22594         
22595         if (nodeIsBefore && nodeIsAfter) {
22596             return 0; // outer
22597         }
22598         if (!nodeIsBefore && nodeIsAfter) {
22599             return 1; //right trailed.
22600         }
22601         
22602         if (nodeIsBefore && !nodeIsAfter) {
22603             return 2;  // left trailed.
22604         }
22605         // fully contined.
22606         return 3;
22607     },
22608
22609     // private? - in a new class?
22610     cleanUpPaste :  function()
22611     {
22612         // cleans up the whole document..
22613         Roo.log('cleanuppaste');
22614         
22615         this.cleanUpChildren(this.doc.body);
22616         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22617         if (clean != this.doc.body.innerHTML) {
22618             this.doc.body.innerHTML = clean;
22619         }
22620         
22621     },
22622     
22623     cleanWordChars : function(input) {// change the chars to hex code
22624         var he = Roo.HtmlEditorCore;
22625         
22626         var output = input;
22627         Roo.each(he.swapCodes, function(sw) { 
22628             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22629             
22630             output = output.replace(swapper, sw[1]);
22631         });
22632         
22633         return output;
22634     },
22635     
22636     
22637     cleanUpChildren : function (n)
22638     {
22639         if (!n.childNodes.length) {
22640             return;
22641         }
22642         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22643            this.cleanUpChild(n.childNodes[i]);
22644         }
22645     },
22646     
22647     
22648         
22649     
22650     cleanUpChild : function (node)
22651     {
22652         var ed = this;
22653         //console.log(node);
22654         if (node.nodeName == "#text") {
22655             // clean up silly Windows -- stuff?
22656             return; 
22657         }
22658         if (node.nodeName == "#comment") {
22659             node.parentNode.removeChild(node);
22660             // clean up silly Windows -- stuff?
22661             return; 
22662         }
22663         var lcname = node.tagName.toLowerCase();
22664         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22665         // whitelist of tags..
22666         
22667         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22668             // remove node.
22669             node.parentNode.removeChild(node);
22670             return;
22671             
22672         }
22673         
22674         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22675         
22676         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22677         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22678         
22679         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22680         //    remove_keep_children = true;
22681         //}
22682         
22683         if (remove_keep_children) {
22684             this.cleanUpChildren(node);
22685             // inserts everything just before this node...
22686             while (node.childNodes.length) {
22687                 var cn = node.childNodes[0];
22688                 node.removeChild(cn);
22689                 node.parentNode.insertBefore(cn, node);
22690             }
22691             node.parentNode.removeChild(node);
22692             return;
22693         }
22694         
22695         if (!node.attributes || !node.attributes.length) {
22696             this.cleanUpChildren(node);
22697             return;
22698         }
22699         
22700         function cleanAttr(n,v)
22701         {
22702             
22703             if (v.match(/^\./) || v.match(/^\//)) {
22704                 return;
22705             }
22706             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22707                 return;
22708             }
22709             if (v.match(/^#/)) {
22710                 return;
22711             }
22712 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22713             node.removeAttribute(n);
22714             
22715         }
22716         
22717         var cwhite = this.cwhite;
22718         var cblack = this.cblack;
22719             
22720         function cleanStyle(n,v)
22721         {
22722             if (v.match(/expression/)) { //XSS?? should we even bother..
22723                 node.removeAttribute(n);
22724                 return;
22725             }
22726             
22727             var parts = v.split(/;/);
22728             var clean = [];
22729             
22730             Roo.each(parts, function(p) {
22731                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22732                 if (!p.length) {
22733                     return true;
22734                 }
22735                 var l = p.split(':').shift().replace(/\s+/g,'');
22736                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22737                 
22738                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22739 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22740                     //node.removeAttribute(n);
22741                     return true;
22742                 }
22743                 //Roo.log()
22744                 // only allow 'c whitelisted system attributes'
22745                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22746 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22747                     //node.removeAttribute(n);
22748                     return true;
22749                 }
22750                 
22751                 
22752                  
22753                 
22754                 clean.push(p);
22755                 return true;
22756             });
22757             if (clean.length) { 
22758                 node.setAttribute(n, clean.join(';'));
22759             } else {
22760                 node.removeAttribute(n);
22761             }
22762             
22763         }
22764         
22765         
22766         for (var i = node.attributes.length-1; i > -1 ; i--) {
22767             var a = node.attributes[i];
22768             //console.log(a);
22769             
22770             if (a.name.toLowerCase().substr(0,2)=='on')  {
22771                 node.removeAttribute(a.name);
22772                 continue;
22773             }
22774             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22775                 node.removeAttribute(a.name);
22776                 continue;
22777             }
22778             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22779                 cleanAttr(a.name,a.value); // fixme..
22780                 continue;
22781             }
22782             if (a.name == 'style') {
22783                 cleanStyle(a.name,a.value);
22784                 continue;
22785             }
22786             /// clean up MS crap..
22787             // tecnically this should be a list of valid class'es..
22788             
22789             
22790             if (a.name == 'class') {
22791                 if (a.value.match(/^Mso/)) {
22792                     node.className = '';
22793                 }
22794                 
22795                 if (a.value.match(/^body$/)) {
22796                     node.className = '';
22797                 }
22798                 continue;
22799             }
22800             
22801             // style cleanup!?
22802             // class cleanup?
22803             
22804         }
22805         
22806         
22807         this.cleanUpChildren(node);
22808         
22809         
22810     },
22811     
22812     /**
22813      * Clean up MS wordisms...
22814      */
22815     cleanWord : function(node)
22816     {
22817         
22818         
22819         if (!node) {
22820             this.cleanWord(this.doc.body);
22821             return;
22822         }
22823         if (node.nodeName == "#text") {
22824             // clean up silly Windows -- stuff?
22825             return; 
22826         }
22827         if (node.nodeName == "#comment") {
22828             node.parentNode.removeChild(node);
22829             // clean up silly Windows -- stuff?
22830             return; 
22831         }
22832         
22833         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22834             node.parentNode.removeChild(node);
22835             return;
22836         }
22837         
22838         // remove - but keep children..
22839         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22840             while (node.childNodes.length) {
22841                 var cn = node.childNodes[0];
22842                 node.removeChild(cn);
22843                 node.parentNode.insertBefore(cn, node);
22844             }
22845             node.parentNode.removeChild(node);
22846             this.iterateChildren(node, this.cleanWord);
22847             return;
22848         }
22849         // clean styles
22850         if (node.className.length) {
22851             
22852             var cn = node.className.split(/\W+/);
22853             var cna = [];
22854             Roo.each(cn, function(cls) {
22855                 if (cls.match(/Mso[a-zA-Z]+/)) {
22856                     return;
22857                 }
22858                 cna.push(cls);
22859             });
22860             node.className = cna.length ? cna.join(' ') : '';
22861             if (!cna.length) {
22862                 node.removeAttribute("class");
22863             }
22864         }
22865         
22866         if (node.hasAttribute("lang")) {
22867             node.removeAttribute("lang");
22868         }
22869         
22870         if (node.hasAttribute("style")) {
22871             
22872             var styles = node.getAttribute("style").split(";");
22873             var nstyle = [];
22874             Roo.each(styles, function(s) {
22875                 if (!s.match(/:/)) {
22876                     return;
22877                 }
22878                 var kv = s.split(":");
22879                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22880                     return;
22881                 }
22882                 // what ever is left... we allow.
22883                 nstyle.push(s);
22884             });
22885             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22886             if (!nstyle.length) {
22887                 node.removeAttribute('style');
22888             }
22889         }
22890         this.iterateChildren(node, this.cleanWord);
22891         
22892         
22893         
22894     },
22895     /**
22896      * iterateChildren of a Node, calling fn each time, using this as the scole..
22897      * @param {DomNode} node node to iterate children of.
22898      * @param {Function} fn method of this class to call on each item.
22899      */
22900     iterateChildren : function(node, fn)
22901     {
22902         if (!node.childNodes.length) {
22903                 return;
22904         }
22905         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22906            fn.call(this, node.childNodes[i])
22907         }
22908     },
22909     
22910     
22911     /**
22912      * cleanTableWidths.
22913      *
22914      * Quite often pasting from word etc.. results in tables with column and widths.
22915      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22916      *
22917      */
22918     cleanTableWidths : function(node)
22919     {
22920          
22921          
22922         if (!node) {
22923             this.cleanTableWidths(this.doc.body);
22924             return;
22925         }
22926         
22927         // ignore list...
22928         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22929             return; 
22930         }
22931         Roo.log(node.tagName);
22932         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22933             this.iterateChildren(node, this.cleanTableWidths);
22934             return;
22935         }
22936         if (node.hasAttribute('width')) {
22937             node.removeAttribute('width');
22938         }
22939         
22940          
22941         if (node.hasAttribute("style")) {
22942             // pretty basic...
22943             
22944             var styles = node.getAttribute("style").split(";");
22945             var nstyle = [];
22946             Roo.each(styles, function(s) {
22947                 if (!s.match(/:/)) {
22948                     return;
22949                 }
22950                 var kv = s.split(":");
22951                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22952                     return;
22953                 }
22954                 // what ever is left... we allow.
22955                 nstyle.push(s);
22956             });
22957             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22958             if (!nstyle.length) {
22959                 node.removeAttribute('style');
22960             }
22961         }
22962         
22963         this.iterateChildren(node, this.cleanTableWidths);
22964         
22965         
22966     },
22967     
22968     
22969     
22970     
22971     domToHTML : function(currentElement, depth, nopadtext) {
22972         
22973         depth = depth || 0;
22974         nopadtext = nopadtext || false;
22975     
22976         if (!currentElement) {
22977             return this.domToHTML(this.doc.body);
22978         }
22979         
22980         //Roo.log(currentElement);
22981         var j;
22982         var allText = false;
22983         var nodeName = currentElement.nodeName;
22984         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22985         
22986         if  (nodeName == '#text') {
22987             
22988             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22989         }
22990         
22991         
22992         var ret = '';
22993         if (nodeName != 'BODY') {
22994              
22995             var i = 0;
22996             // Prints the node tagName, such as <A>, <IMG>, etc
22997             if (tagName) {
22998                 var attr = [];
22999                 for(i = 0; i < currentElement.attributes.length;i++) {
23000                     // quoting?
23001                     var aname = currentElement.attributes.item(i).name;
23002                     if (!currentElement.attributes.item(i).value.length) {
23003                         continue;
23004                     }
23005                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23006                 }
23007                 
23008                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23009             } 
23010             else {
23011                 
23012                 // eack
23013             }
23014         } else {
23015             tagName = false;
23016         }
23017         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23018             return ret;
23019         }
23020         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23021             nopadtext = true;
23022         }
23023         
23024         
23025         // Traverse the tree
23026         i = 0;
23027         var currentElementChild = currentElement.childNodes.item(i);
23028         var allText = true;
23029         var innerHTML  = '';
23030         lastnode = '';
23031         while (currentElementChild) {
23032             // Formatting code (indent the tree so it looks nice on the screen)
23033             var nopad = nopadtext;
23034             if (lastnode == 'SPAN') {
23035                 nopad  = true;
23036             }
23037             // text
23038             if  (currentElementChild.nodeName == '#text') {
23039                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23040                 toadd = nopadtext ? toadd : toadd.trim();
23041                 if (!nopad && toadd.length > 80) {
23042                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23043                 }
23044                 innerHTML  += toadd;
23045                 
23046                 i++;
23047                 currentElementChild = currentElement.childNodes.item(i);
23048                 lastNode = '';
23049                 continue;
23050             }
23051             allText = false;
23052             
23053             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23054                 
23055             // Recursively traverse the tree structure of the child node
23056             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23057             lastnode = currentElementChild.nodeName;
23058             i++;
23059             currentElementChild=currentElement.childNodes.item(i);
23060         }
23061         
23062         ret += innerHTML;
23063         
23064         if (!allText) {
23065                 // The remaining code is mostly for formatting the tree
23066             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23067         }
23068         
23069         
23070         if (tagName) {
23071             ret+= "</"+tagName+">";
23072         }
23073         return ret;
23074         
23075     },
23076         
23077     applyBlacklists : function()
23078     {
23079         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23080         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23081         
23082         this.white = [];
23083         this.black = [];
23084         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23085             if (b.indexOf(tag) > -1) {
23086                 return;
23087             }
23088             this.white.push(tag);
23089             
23090         }, this);
23091         
23092         Roo.each(w, function(tag) {
23093             if (b.indexOf(tag) > -1) {
23094                 return;
23095             }
23096             if (this.white.indexOf(tag) > -1) {
23097                 return;
23098             }
23099             this.white.push(tag);
23100             
23101         }, this);
23102         
23103         
23104         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23105             if (w.indexOf(tag) > -1) {
23106                 return;
23107             }
23108             this.black.push(tag);
23109             
23110         }, this);
23111         
23112         Roo.each(b, function(tag) {
23113             if (w.indexOf(tag) > -1) {
23114                 return;
23115             }
23116             if (this.black.indexOf(tag) > -1) {
23117                 return;
23118             }
23119             this.black.push(tag);
23120             
23121         }, this);
23122         
23123         
23124         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23125         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23126         
23127         this.cwhite = [];
23128         this.cblack = [];
23129         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23130             if (b.indexOf(tag) > -1) {
23131                 return;
23132             }
23133             this.cwhite.push(tag);
23134             
23135         }, this);
23136         
23137         Roo.each(w, function(tag) {
23138             if (b.indexOf(tag) > -1) {
23139                 return;
23140             }
23141             if (this.cwhite.indexOf(tag) > -1) {
23142                 return;
23143             }
23144             this.cwhite.push(tag);
23145             
23146         }, this);
23147         
23148         
23149         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23150             if (w.indexOf(tag) > -1) {
23151                 return;
23152             }
23153             this.cblack.push(tag);
23154             
23155         }, this);
23156         
23157         Roo.each(b, function(tag) {
23158             if (w.indexOf(tag) > -1) {
23159                 return;
23160             }
23161             if (this.cblack.indexOf(tag) > -1) {
23162                 return;
23163             }
23164             this.cblack.push(tag);
23165             
23166         }, this);
23167     },
23168     
23169     setStylesheets : function(stylesheets)
23170     {
23171         if(typeof(stylesheets) == 'string'){
23172             Roo.get(this.iframe.contentDocument.head).createChild({
23173                 tag : 'link',
23174                 rel : 'stylesheet',
23175                 type : 'text/css',
23176                 href : stylesheets
23177             });
23178             
23179             return;
23180         }
23181         var _this = this;
23182      
23183         Roo.each(stylesheets, function(s) {
23184             if(!s.length){
23185                 return;
23186             }
23187             
23188             Roo.get(_this.iframe.contentDocument.head).createChild({
23189                 tag : 'link',
23190                 rel : 'stylesheet',
23191                 type : 'text/css',
23192                 href : s
23193             });
23194         });
23195
23196         
23197     },
23198     
23199     removeStylesheets : function()
23200     {
23201         var _this = this;
23202         
23203         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23204             s.remove();
23205         });
23206     },
23207     
23208     setStyle : function(style)
23209     {
23210         Roo.get(this.iframe.contentDocument.head).createChild({
23211             tag : 'style',
23212             type : 'text/css',
23213             html : style
23214         });
23215
23216         return;
23217     }
23218     
23219     // hide stuff that is not compatible
23220     /**
23221      * @event blur
23222      * @hide
23223      */
23224     /**
23225      * @event change
23226      * @hide
23227      */
23228     /**
23229      * @event focus
23230      * @hide
23231      */
23232     /**
23233      * @event specialkey
23234      * @hide
23235      */
23236     /**
23237      * @cfg {String} fieldClass @hide
23238      */
23239     /**
23240      * @cfg {String} focusClass @hide
23241      */
23242     /**
23243      * @cfg {String} autoCreate @hide
23244      */
23245     /**
23246      * @cfg {String} inputType @hide
23247      */
23248     /**
23249      * @cfg {String} invalidClass @hide
23250      */
23251     /**
23252      * @cfg {String} invalidText @hide
23253      */
23254     /**
23255      * @cfg {String} msgFx @hide
23256      */
23257     /**
23258      * @cfg {String} validateOnBlur @hide
23259      */
23260 });
23261
23262 Roo.HtmlEditorCore.white = [
23263         'area', 'br', 'img', 'input', 'hr', 'wbr',
23264         
23265        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23266        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23267        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23268        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23269        'table',   'ul',         'xmp', 
23270        
23271        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23272       'thead',   'tr', 
23273      
23274       'dir', 'menu', 'ol', 'ul', 'dl',
23275        
23276       'embed',  'object'
23277 ];
23278
23279
23280 Roo.HtmlEditorCore.black = [
23281     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23282         'applet', // 
23283         'base',   'basefont', 'bgsound', 'blink',  'body', 
23284         'frame',  'frameset', 'head',    'html',   'ilayer', 
23285         'iframe', 'layer',  'link',     'meta',    'object',   
23286         'script', 'style' ,'title',  'xml' // clean later..
23287 ];
23288 Roo.HtmlEditorCore.clean = [
23289     'script', 'style', 'title', 'xml'
23290 ];
23291 Roo.HtmlEditorCore.remove = [
23292     'font'
23293 ];
23294 // attributes..
23295
23296 Roo.HtmlEditorCore.ablack = [
23297     'on'
23298 ];
23299     
23300 Roo.HtmlEditorCore.aclean = [ 
23301     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23302 ];
23303
23304 // protocols..
23305 Roo.HtmlEditorCore.pwhite= [
23306         'http',  'https',  'mailto'
23307 ];
23308
23309 // white listed style attributes.
23310 Roo.HtmlEditorCore.cwhite= [
23311       //  'text-align', /// default is to allow most things..
23312       
23313          
23314 //        'font-size'//??
23315 ];
23316
23317 // black listed style attributes.
23318 Roo.HtmlEditorCore.cblack= [
23319       //  'font-size' -- this can be set by the project 
23320 ];
23321
23322
23323 Roo.HtmlEditorCore.swapCodes   =[ 
23324     [    8211, "--" ], 
23325     [    8212, "--" ], 
23326     [    8216,  "'" ],  
23327     [    8217, "'" ],  
23328     [    8220, '"' ],  
23329     [    8221, '"' ],  
23330     [    8226, "*" ],  
23331     [    8230, "..." ]
23332 ]; 
23333
23334     /*
23335  * - LGPL
23336  *
23337  * HtmlEditor
23338  * 
23339  */
23340
23341 /**
23342  * @class Roo.bootstrap.HtmlEditor
23343  * @extends Roo.bootstrap.TextArea
23344  * Bootstrap HtmlEditor class
23345
23346  * @constructor
23347  * Create a new HtmlEditor
23348  * @param {Object} config The config object
23349  */
23350
23351 Roo.bootstrap.HtmlEditor = function(config){
23352     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23353     if (!this.toolbars) {
23354         this.toolbars = [];
23355     }
23356     
23357     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23358     this.addEvents({
23359             /**
23360              * @event initialize
23361              * Fires when the editor is fully initialized (including the iframe)
23362              * @param {HtmlEditor} this
23363              */
23364             initialize: true,
23365             /**
23366              * @event activate
23367              * Fires when the editor is first receives the focus. Any insertion must wait
23368              * until after this event.
23369              * @param {HtmlEditor} this
23370              */
23371             activate: true,
23372              /**
23373              * @event beforesync
23374              * Fires before the textarea is updated with content from the editor iframe. Return false
23375              * to cancel the sync.
23376              * @param {HtmlEditor} this
23377              * @param {String} html
23378              */
23379             beforesync: true,
23380              /**
23381              * @event beforepush
23382              * Fires before the iframe editor is updated with content from the textarea. Return false
23383              * to cancel the push.
23384              * @param {HtmlEditor} this
23385              * @param {String} html
23386              */
23387             beforepush: true,
23388              /**
23389              * @event sync
23390              * Fires when the textarea is updated with content from the editor iframe.
23391              * @param {HtmlEditor} this
23392              * @param {String} html
23393              */
23394             sync: true,
23395              /**
23396              * @event push
23397              * Fires when the iframe editor is updated with content from the textarea.
23398              * @param {HtmlEditor} this
23399              * @param {String} html
23400              */
23401             push: true,
23402              /**
23403              * @event editmodechange
23404              * Fires when the editor switches edit modes
23405              * @param {HtmlEditor} this
23406              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23407              */
23408             editmodechange: true,
23409             /**
23410              * @event editorevent
23411              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23412              * @param {HtmlEditor} this
23413              */
23414             editorevent: true,
23415             /**
23416              * @event firstfocus
23417              * Fires when on first focus - needed by toolbars..
23418              * @param {HtmlEditor} this
23419              */
23420             firstfocus: true,
23421             /**
23422              * @event autosave
23423              * Auto save the htmlEditor value as a file into Events
23424              * @param {HtmlEditor} this
23425              */
23426             autosave: true,
23427             /**
23428              * @event savedpreview
23429              * preview the saved version of htmlEditor
23430              * @param {HtmlEditor} this
23431              */
23432             savedpreview: true
23433         });
23434 };
23435
23436
23437 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23438     
23439     
23440       /**
23441      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23442      */
23443     toolbars : false,
23444     
23445      /**
23446     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23447     */
23448     btns : [],
23449    
23450      /**
23451      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23452      *                        Roo.resizable.
23453      */
23454     resizable : false,
23455      /**
23456      * @cfg {Number} height (in pixels)
23457      */   
23458     height: 300,
23459    /**
23460      * @cfg {Number} width (in pixels)
23461      */   
23462     width: false,
23463     
23464     /**
23465      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23466      * 
23467      */
23468     stylesheets: false,
23469     
23470     // id of frame..
23471     frameId: false,
23472     
23473     // private properties
23474     validationEvent : false,
23475     deferHeight: true,
23476     initialized : false,
23477     activated : false,
23478     
23479     onFocus : Roo.emptyFn,
23480     iframePad:3,
23481     hideMode:'offsets',
23482     
23483     tbContainer : false,
23484     
23485     bodyCls : '',
23486     
23487     toolbarContainer :function() {
23488         return this.wrap.select('.x-html-editor-tb',true).first();
23489     },
23490
23491     /**
23492      * Protected method that will not generally be called directly. It
23493      * is called when the editor creates its toolbar. Override this method if you need to
23494      * add custom toolbar buttons.
23495      * @param {HtmlEditor} editor
23496      */
23497     createToolbar : function(){
23498         Roo.log('renewing');
23499         Roo.log("create toolbars");
23500         
23501         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23502         this.toolbars[0].render(this.toolbarContainer());
23503         
23504         return;
23505         
23506 //        if (!editor.toolbars || !editor.toolbars.length) {
23507 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23508 //        }
23509 //        
23510 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23511 //            editor.toolbars[i] = Roo.factory(
23512 //                    typeof(editor.toolbars[i]) == 'string' ?
23513 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23514 //                Roo.bootstrap.HtmlEditor);
23515 //            editor.toolbars[i].init(editor);
23516 //        }
23517     },
23518
23519      
23520     // private
23521     onRender : function(ct, position)
23522     {
23523        // Roo.log("Call onRender: " + this.xtype);
23524         var _t = this;
23525         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23526       
23527         this.wrap = this.inputEl().wrap({
23528             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23529         });
23530         
23531         this.editorcore.onRender(ct, position);
23532          
23533         if (this.resizable) {
23534             this.resizeEl = new Roo.Resizable(this.wrap, {
23535                 pinned : true,
23536                 wrap: true,
23537                 dynamic : true,
23538                 minHeight : this.height,
23539                 height: this.height,
23540                 handles : this.resizable,
23541                 width: this.width,
23542                 listeners : {
23543                     resize : function(r, w, h) {
23544                         _t.onResize(w,h); // -something
23545                     }
23546                 }
23547             });
23548             
23549         }
23550         this.createToolbar(this);
23551        
23552         
23553         if(!this.width && this.resizable){
23554             this.setSize(this.wrap.getSize());
23555         }
23556         if (this.resizeEl) {
23557             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23558             // should trigger onReize..
23559         }
23560         
23561     },
23562
23563     // private
23564     onResize : function(w, h)
23565     {
23566         Roo.log('resize: ' +w + ',' + h );
23567         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23568         var ew = false;
23569         var eh = false;
23570         
23571         if(this.inputEl() ){
23572             if(typeof w == 'number'){
23573                 var aw = w - this.wrap.getFrameWidth('lr');
23574                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23575                 ew = aw;
23576             }
23577             if(typeof h == 'number'){
23578                  var tbh = -11;  // fixme it needs to tool bar size!
23579                 for (var i =0; i < this.toolbars.length;i++) {
23580                     // fixme - ask toolbars for heights?
23581                     tbh += this.toolbars[i].el.getHeight();
23582                     //if (this.toolbars[i].footer) {
23583                     //    tbh += this.toolbars[i].footer.el.getHeight();
23584                     //}
23585                 }
23586               
23587                 
23588                 
23589                 
23590                 
23591                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23592                 ah -= 5; // knock a few pixes off for look..
23593                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23594                 var eh = ah;
23595             }
23596         }
23597         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23598         this.editorcore.onResize(ew,eh);
23599         
23600     },
23601
23602     /**
23603      * Toggles the editor between standard and source edit mode.
23604      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23605      */
23606     toggleSourceEdit : function(sourceEditMode)
23607     {
23608         this.editorcore.toggleSourceEdit(sourceEditMode);
23609         
23610         if(this.editorcore.sourceEditMode){
23611             Roo.log('editor - showing textarea');
23612             
23613 //            Roo.log('in');
23614 //            Roo.log(this.syncValue());
23615             this.syncValue();
23616             this.inputEl().removeClass(['hide', 'x-hidden']);
23617             this.inputEl().dom.removeAttribute('tabIndex');
23618             this.inputEl().focus();
23619         }else{
23620             Roo.log('editor - hiding textarea');
23621 //            Roo.log('out')
23622 //            Roo.log(this.pushValue()); 
23623             this.pushValue();
23624             
23625             this.inputEl().addClass(['hide', 'x-hidden']);
23626             this.inputEl().dom.setAttribute('tabIndex', -1);
23627             //this.deferFocus();
23628         }
23629          
23630         if(this.resizable){
23631             this.setSize(this.wrap.getSize());
23632         }
23633         
23634         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23635     },
23636  
23637     // private (for BoxComponent)
23638     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23639
23640     // private (for BoxComponent)
23641     getResizeEl : function(){
23642         return this.wrap;
23643     },
23644
23645     // private (for BoxComponent)
23646     getPositionEl : function(){
23647         return this.wrap;
23648     },
23649
23650     // private
23651     initEvents : function(){
23652         this.originalValue = this.getValue();
23653     },
23654
23655 //    /**
23656 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23657 //     * @method
23658 //     */
23659 //    markInvalid : Roo.emptyFn,
23660 //    /**
23661 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23662 //     * @method
23663 //     */
23664 //    clearInvalid : Roo.emptyFn,
23665
23666     setValue : function(v){
23667         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23668         this.editorcore.pushValue();
23669     },
23670
23671      
23672     // private
23673     deferFocus : function(){
23674         this.focus.defer(10, this);
23675     },
23676
23677     // doc'ed in Field
23678     focus : function(){
23679         this.editorcore.focus();
23680         
23681     },
23682       
23683
23684     // private
23685     onDestroy : function(){
23686         
23687         
23688         
23689         if(this.rendered){
23690             
23691             for (var i =0; i < this.toolbars.length;i++) {
23692                 // fixme - ask toolbars for heights?
23693                 this.toolbars[i].onDestroy();
23694             }
23695             
23696             this.wrap.dom.innerHTML = '';
23697             this.wrap.remove();
23698         }
23699     },
23700
23701     // private
23702     onFirstFocus : function(){
23703         //Roo.log("onFirstFocus");
23704         this.editorcore.onFirstFocus();
23705          for (var i =0; i < this.toolbars.length;i++) {
23706             this.toolbars[i].onFirstFocus();
23707         }
23708         
23709     },
23710     
23711     // private
23712     syncValue : function()
23713     {   
23714         this.editorcore.syncValue();
23715     },
23716     
23717     pushValue : function()
23718     {   
23719         this.editorcore.pushValue();
23720     }
23721      
23722     
23723     // hide stuff that is not compatible
23724     /**
23725      * @event blur
23726      * @hide
23727      */
23728     /**
23729      * @event change
23730      * @hide
23731      */
23732     /**
23733      * @event focus
23734      * @hide
23735      */
23736     /**
23737      * @event specialkey
23738      * @hide
23739      */
23740     /**
23741      * @cfg {String} fieldClass @hide
23742      */
23743     /**
23744      * @cfg {String} focusClass @hide
23745      */
23746     /**
23747      * @cfg {String} autoCreate @hide
23748      */
23749     /**
23750      * @cfg {String} inputType @hide
23751      */
23752     /**
23753      * @cfg {String} invalidClass @hide
23754      */
23755     /**
23756      * @cfg {String} invalidText @hide
23757      */
23758     /**
23759      * @cfg {String} msgFx @hide
23760      */
23761     /**
23762      * @cfg {String} validateOnBlur @hide
23763      */
23764 });
23765  
23766     
23767    
23768    
23769    
23770       
23771 Roo.namespace('Roo.bootstrap.htmleditor');
23772 /**
23773  * @class Roo.bootstrap.HtmlEditorToolbar1
23774  * Basic Toolbar
23775  * 
23776  * Usage:
23777  *
23778  new Roo.bootstrap.HtmlEditor({
23779     ....
23780     toolbars : [
23781         new Roo.bootstrap.HtmlEditorToolbar1({
23782             disable : { fonts: 1 , format: 1, ..., ... , ...],
23783             btns : [ .... ]
23784         })
23785     }
23786      
23787  * 
23788  * @cfg {Object} disable List of elements to disable..
23789  * @cfg {Array} btns List of additional buttons.
23790  * 
23791  * 
23792  * NEEDS Extra CSS? 
23793  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23794  */
23795  
23796 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23797 {
23798     
23799     Roo.apply(this, config);
23800     
23801     // default disabled, based on 'good practice'..
23802     this.disable = this.disable || {};
23803     Roo.applyIf(this.disable, {
23804         fontSize : true,
23805         colors : true,
23806         specialElements : true
23807     });
23808     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23809     
23810     this.editor = config.editor;
23811     this.editorcore = config.editor.editorcore;
23812     
23813     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23814     
23815     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23816     // dont call parent... till later.
23817 }
23818 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23819      
23820     bar : true,
23821     
23822     editor : false,
23823     editorcore : false,
23824     
23825     
23826     formats : [
23827         "p" ,  
23828         "h1","h2","h3","h4","h5","h6", 
23829         "pre", "code", 
23830         "abbr", "acronym", "address", "cite", "samp", "var",
23831         'div','span'
23832     ],
23833     
23834     onRender : function(ct, position)
23835     {
23836        // Roo.log("Call onRender: " + this.xtype);
23837         
23838        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23839        Roo.log(this.el);
23840        this.el.dom.style.marginBottom = '0';
23841        var _this = this;
23842        var editorcore = this.editorcore;
23843        var editor= this.editor;
23844        
23845        var children = [];
23846        var btn = function(id,cmd , toggle, handler, html){
23847        
23848             var  event = toggle ? 'toggle' : 'click';
23849        
23850             var a = {
23851                 size : 'sm',
23852                 xtype: 'Button',
23853                 xns: Roo.bootstrap,
23854                 glyphicon : id,
23855                 cmd : id || cmd,
23856                 enableToggle:toggle !== false,
23857                 html : html || '',
23858                 pressed : toggle ? false : null,
23859                 listeners : {}
23860             };
23861             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23862                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23863             };
23864             children.push(a);
23865             return a;
23866        }
23867        
23868     //    var cb_box = function...
23869         
23870         var style = {
23871                 xtype: 'Button',
23872                 size : 'sm',
23873                 xns: Roo.bootstrap,
23874                 glyphicon : 'font',
23875                 //html : 'submit'
23876                 menu : {
23877                     xtype: 'Menu',
23878                     xns: Roo.bootstrap,
23879                     items:  []
23880                 }
23881         };
23882         Roo.each(this.formats, function(f) {
23883             style.menu.items.push({
23884                 xtype :'MenuItem',
23885                 xns: Roo.bootstrap,
23886                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23887                 tagname : f,
23888                 listeners : {
23889                     click : function()
23890                     {
23891                         editorcore.insertTag(this.tagname);
23892                         editor.focus();
23893                     }
23894                 }
23895                 
23896             });
23897         });
23898         children.push(style);   
23899         
23900         btn('bold',false,true);
23901         btn('italic',false,true);
23902         btn('align-left', 'justifyleft',true);
23903         btn('align-center', 'justifycenter',true);
23904         btn('align-right' , 'justifyright',true);
23905         btn('link', false, false, function(btn) {
23906             //Roo.log("create link?");
23907             var url = prompt(this.createLinkText, this.defaultLinkValue);
23908             if(url && url != 'http:/'+'/'){
23909                 this.editorcore.relayCmd('createlink', url);
23910             }
23911         }),
23912         btn('list','insertunorderedlist',true);
23913         btn('pencil', false,true, function(btn){
23914                 Roo.log(this);
23915                 this.toggleSourceEdit(btn.pressed);
23916         });
23917         
23918         if (this.editor.btns.length > 0) {
23919             for (var i = 0; i<this.editor.btns.length; i++) {
23920                 children.push(this.editor.btns[i]);
23921             }
23922         }
23923         
23924         /*
23925         var cog = {
23926                 xtype: 'Button',
23927                 size : 'sm',
23928                 xns: Roo.bootstrap,
23929                 glyphicon : 'cog',
23930                 //html : 'submit'
23931                 menu : {
23932                     xtype: 'Menu',
23933                     xns: Roo.bootstrap,
23934                     items:  []
23935                 }
23936         };
23937         
23938         cog.menu.items.push({
23939             xtype :'MenuItem',
23940             xns: Roo.bootstrap,
23941             html : Clean styles,
23942             tagname : f,
23943             listeners : {
23944                 click : function()
23945                 {
23946                     editorcore.insertTag(this.tagname);
23947                     editor.focus();
23948                 }
23949             }
23950             
23951         });
23952        */
23953         
23954          
23955        this.xtype = 'NavSimplebar';
23956         
23957         for(var i=0;i< children.length;i++) {
23958             
23959             this.buttons.add(this.addxtypeChild(children[i]));
23960             
23961         }
23962         
23963         editor.on('editorevent', this.updateToolbar, this);
23964     },
23965     onBtnClick : function(id)
23966     {
23967        this.editorcore.relayCmd(id);
23968        this.editorcore.focus();
23969     },
23970     
23971     /**
23972      * Protected method that will not generally be called directly. It triggers
23973      * a toolbar update by reading the markup state of the current selection in the editor.
23974      */
23975     updateToolbar: function(){
23976
23977         if(!this.editorcore.activated){
23978             this.editor.onFirstFocus(); // is this neeed?
23979             return;
23980         }
23981
23982         var btns = this.buttons; 
23983         var doc = this.editorcore.doc;
23984         btns.get('bold').setActive(doc.queryCommandState('bold'));
23985         btns.get('italic').setActive(doc.queryCommandState('italic'));
23986         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23987         
23988         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23989         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23990         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23991         
23992         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23993         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23994          /*
23995         
23996         var ans = this.editorcore.getAllAncestors();
23997         if (this.formatCombo) {
23998             
23999             
24000             var store = this.formatCombo.store;
24001             this.formatCombo.setValue("");
24002             for (var i =0; i < ans.length;i++) {
24003                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24004                     // select it..
24005                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24006                     break;
24007                 }
24008             }
24009         }
24010         
24011         
24012         
24013         // hides menus... - so this cant be on a menu...
24014         Roo.bootstrap.MenuMgr.hideAll();
24015         */
24016         Roo.bootstrap.MenuMgr.hideAll();
24017         //this.editorsyncValue();
24018     },
24019     onFirstFocus: function() {
24020         this.buttons.each(function(item){
24021            item.enable();
24022         });
24023     },
24024     toggleSourceEdit : function(sourceEditMode){
24025         
24026           
24027         if(sourceEditMode){
24028             Roo.log("disabling buttons");
24029            this.buttons.each( function(item){
24030                 if(item.cmd != 'pencil'){
24031                     item.disable();
24032                 }
24033             });
24034           
24035         }else{
24036             Roo.log("enabling buttons");
24037             if(this.editorcore.initialized){
24038                 this.buttons.each( function(item){
24039                     item.enable();
24040                 });
24041             }
24042             
24043         }
24044         Roo.log("calling toggole on editor");
24045         // tell the editor that it's been pressed..
24046         this.editor.toggleSourceEdit(sourceEditMode);
24047        
24048     }
24049 });
24050
24051
24052
24053
24054
24055 /**
24056  * @class Roo.bootstrap.Table.AbstractSelectionModel
24057  * @extends Roo.util.Observable
24058  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24059  * implemented by descendant classes.  This class should not be directly instantiated.
24060  * @constructor
24061  */
24062 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24063     this.locked = false;
24064     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24065 };
24066
24067
24068 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24069     /** @ignore Called by the grid automatically. Do not call directly. */
24070     init : function(grid){
24071         this.grid = grid;
24072         this.initEvents();
24073     },
24074
24075     /**
24076      * Locks the selections.
24077      */
24078     lock : function(){
24079         this.locked = true;
24080     },
24081
24082     /**
24083      * Unlocks the selections.
24084      */
24085     unlock : function(){
24086         this.locked = false;
24087     },
24088
24089     /**
24090      * Returns true if the selections are locked.
24091      * @return {Boolean}
24092      */
24093     isLocked : function(){
24094         return this.locked;
24095     }
24096 });
24097 /**
24098  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24099  * @class Roo.bootstrap.Table.RowSelectionModel
24100  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24101  * It supports multiple selections and keyboard selection/navigation. 
24102  * @constructor
24103  * @param {Object} config
24104  */
24105
24106 Roo.bootstrap.Table.RowSelectionModel = function(config){
24107     Roo.apply(this, config);
24108     this.selections = new Roo.util.MixedCollection(false, function(o){
24109         return o.id;
24110     });
24111
24112     this.last = false;
24113     this.lastActive = false;
24114
24115     this.addEvents({
24116         /**
24117              * @event selectionchange
24118              * Fires when the selection changes
24119              * @param {SelectionModel} this
24120              */
24121             "selectionchange" : true,
24122         /**
24123              * @event afterselectionchange
24124              * Fires after the selection changes (eg. by key press or clicking)
24125              * @param {SelectionModel} this
24126              */
24127             "afterselectionchange" : true,
24128         /**
24129              * @event beforerowselect
24130              * Fires when a row is selected being selected, return false to cancel.
24131              * @param {SelectionModel} this
24132              * @param {Number} rowIndex The selected index
24133              * @param {Boolean} keepExisting False if other selections will be cleared
24134              */
24135             "beforerowselect" : true,
24136         /**
24137              * @event rowselect
24138              * Fires when a row is selected.
24139              * @param {SelectionModel} this
24140              * @param {Number} rowIndex The selected index
24141              * @param {Roo.data.Record} r The record
24142              */
24143             "rowselect" : true,
24144         /**
24145              * @event rowdeselect
24146              * Fires when a row is deselected.
24147              * @param {SelectionModel} this
24148              * @param {Number} rowIndex The selected index
24149              */
24150         "rowdeselect" : true
24151     });
24152     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24153     this.locked = false;
24154  };
24155
24156 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24157     /**
24158      * @cfg {Boolean} singleSelect
24159      * True to allow selection of only one row at a time (defaults to false)
24160      */
24161     singleSelect : false,
24162
24163     // private
24164     initEvents : function()
24165     {
24166
24167         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24168         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24169         //}else{ // allow click to work like normal
24170          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24171         //}
24172         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24173         this.grid.on("rowclick", this.handleMouseDown, this);
24174         
24175         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24176             "up" : function(e){
24177                 if(!e.shiftKey){
24178                     this.selectPrevious(e.shiftKey);
24179                 }else if(this.last !== false && this.lastActive !== false){
24180                     var last = this.last;
24181                     this.selectRange(this.last,  this.lastActive-1);
24182                     this.grid.getView().focusRow(this.lastActive);
24183                     if(last !== false){
24184                         this.last = last;
24185                     }
24186                 }else{
24187                     this.selectFirstRow();
24188                 }
24189                 this.fireEvent("afterselectionchange", this);
24190             },
24191             "down" : function(e){
24192                 if(!e.shiftKey){
24193                     this.selectNext(e.shiftKey);
24194                 }else if(this.last !== false && this.lastActive !== false){
24195                     var last = this.last;
24196                     this.selectRange(this.last,  this.lastActive+1);
24197                     this.grid.getView().focusRow(this.lastActive);
24198                     if(last !== false){
24199                         this.last = last;
24200                     }
24201                 }else{
24202                     this.selectFirstRow();
24203                 }
24204                 this.fireEvent("afterselectionchange", this);
24205             },
24206             scope: this
24207         });
24208         this.grid.store.on('load', function(){
24209             this.selections.clear();
24210         },this);
24211         /*
24212         var view = this.grid.view;
24213         view.on("refresh", this.onRefresh, this);
24214         view.on("rowupdated", this.onRowUpdated, this);
24215         view.on("rowremoved", this.onRemove, this);
24216         */
24217     },
24218
24219     // private
24220     onRefresh : function()
24221     {
24222         var ds = this.grid.store, i, v = this.grid.view;
24223         var s = this.selections;
24224         s.each(function(r){
24225             if((i = ds.indexOfId(r.id)) != -1){
24226                 v.onRowSelect(i);
24227             }else{
24228                 s.remove(r);
24229             }
24230         });
24231     },
24232
24233     // private
24234     onRemove : function(v, index, r){
24235         this.selections.remove(r);
24236     },
24237
24238     // private
24239     onRowUpdated : function(v, index, r){
24240         if(this.isSelected(r)){
24241             v.onRowSelect(index);
24242         }
24243     },
24244
24245     /**
24246      * Select records.
24247      * @param {Array} records The records to select
24248      * @param {Boolean} keepExisting (optional) True to keep existing selections
24249      */
24250     selectRecords : function(records, keepExisting)
24251     {
24252         if(!keepExisting){
24253             this.clearSelections();
24254         }
24255             var ds = this.grid.store;
24256         for(var i = 0, len = records.length; i < len; i++){
24257             this.selectRow(ds.indexOf(records[i]), true);
24258         }
24259     },
24260
24261     /**
24262      * Gets the number of selected rows.
24263      * @return {Number}
24264      */
24265     getCount : function(){
24266         return this.selections.length;
24267     },
24268
24269     /**
24270      * Selects the first row in the grid.
24271      */
24272     selectFirstRow : function(){
24273         this.selectRow(0);
24274     },
24275
24276     /**
24277      * Select the last row.
24278      * @param {Boolean} keepExisting (optional) True to keep existing selections
24279      */
24280     selectLastRow : function(keepExisting){
24281         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24282         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24283     },
24284
24285     /**
24286      * Selects the row immediately following the last selected row.
24287      * @param {Boolean} keepExisting (optional) True to keep existing selections
24288      */
24289     selectNext : function(keepExisting)
24290     {
24291             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24292             this.selectRow(this.last+1, keepExisting);
24293             this.grid.getView().focusRow(this.last);
24294         }
24295     },
24296
24297     /**
24298      * Selects the row that precedes the last selected row.
24299      * @param {Boolean} keepExisting (optional) True to keep existing selections
24300      */
24301     selectPrevious : function(keepExisting){
24302         if(this.last){
24303             this.selectRow(this.last-1, keepExisting);
24304             this.grid.getView().focusRow(this.last);
24305         }
24306     },
24307
24308     /**
24309      * Returns the selected records
24310      * @return {Array} Array of selected records
24311      */
24312     getSelections : function(){
24313         return [].concat(this.selections.items);
24314     },
24315
24316     /**
24317      * Returns the first selected record.
24318      * @return {Record}
24319      */
24320     getSelected : function(){
24321         return this.selections.itemAt(0);
24322     },
24323
24324
24325     /**
24326      * Clears all selections.
24327      */
24328     clearSelections : function(fast)
24329     {
24330         if(this.locked) {
24331             return;
24332         }
24333         if(fast !== true){
24334                 var ds = this.grid.store;
24335             var s = this.selections;
24336             s.each(function(r){
24337                 this.deselectRow(ds.indexOfId(r.id));
24338             }, this);
24339             s.clear();
24340         }else{
24341             this.selections.clear();
24342         }
24343         this.last = false;
24344     },
24345
24346
24347     /**
24348      * Selects all rows.
24349      */
24350     selectAll : function(){
24351         if(this.locked) {
24352             return;
24353         }
24354         this.selections.clear();
24355         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24356             this.selectRow(i, true);
24357         }
24358     },
24359
24360     /**
24361      * Returns True if there is a selection.
24362      * @return {Boolean}
24363      */
24364     hasSelection : function(){
24365         return this.selections.length > 0;
24366     },
24367
24368     /**
24369      * Returns True if the specified row is selected.
24370      * @param {Number/Record} record The record or index of the record to check
24371      * @return {Boolean}
24372      */
24373     isSelected : function(index){
24374             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24375         return (r && this.selections.key(r.id) ? true : false);
24376     },
24377
24378     /**
24379      * Returns True if the specified record id is selected.
24380      * @param {String} id The id of record to check
24381      * @return {Boolean}
24382      */
24383     isIdSelected : function(id){
24384         return (this.selections.key(id) ? true : false);
24385     },
24386
24387
24388     // private
24389     handleMouseDBClick : function(e, t){
24390         
24391     },
24392     // private
24393     handleMouseDown : function(e, t)
24394     {
24395             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24396         if(this.isLocked() || rowIndex < 0 ){
24397             return;
24398         };
24399         if(e.shiftKey && this.last !== false){
24400             var last = this.last;
24401             this.selectRange(last, rowIndex, e.ctrlKey);
24402             this.last = last; // reset the last
24403             t.focus();
24404     
24405         }else{
24406             var isSelected = this.isSelected(rowIndex);
24407             //Roo.log("select row:" + rowIndex);
24408             if(isSelected){
24409                 this.deselectRow(rowIndex);
24410             } else {
24411                         this.selectRow(rowIndex, true);
24412             }
24413     
24414             /*
24415                 if(e.button !== 0 && isSelected){
24416                 alert('rowIndex 2: ' + rowIndex);
24417                     view.focusRow(rowIndex);
24418                 }else if(e.ctrlKey && isSelected){
24419                     this.deselectRow(rowIndex);
24420                 }else if(!isSelected){
24421                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24422                     view.focusRow(rowIndex);
24423                 }
24424             */
24425         }
24426         this.fireEvent("afterselectionchange", this);
24427     },
24428     // private
24429     handleDragableRowClick :  function(grid, rowIndex, e) 
24430     {
24431         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24432             this.selectRow(rowIndex, false);
24433             grid.view.focusRow(rowIndex);
24434              this.fireEvent("afterselectionchange", this);
24435         }
24436     },
24437     
24438     /**
24439      * Selects multiple rows.
24440      * @param {Array} rows Array of the indexes of the row to select
24441      * @param {Boolean} keepExisting (optional) True to keep existing selections
24442      */
24443     selectRows : function(rows, keepExisting){
24444         if(!keepExisting){
24445             this.clearSelections();
24446         }
24447         for(var i = 0, len = rows.length; i < len; i++){
24448             this.selectRow(rows[i], true);
24449         }
24450     },
24451
24452     /**
24453      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24454      * @param {Number} startRow The index of the first row in the range
24455      * @param {Number} endRow The index of the last row in the range
24456      * @param {Boolean} keepExisting (optional) True to retain existing selections
24457      */
24458     selectRange : function(startRow, endRow, keepExisting){
24459         if(this.locked) {
24460             return;
24461         }
24462         if(!keepExisting){
24463             this.clearSelections();
24464         }
24465         if(startRow <= endRow){
24466             for(var i = startRow; i <= endRow; i++){
24467                 this.selectRow(i, true);
24468             }
24469         }else{
24470             for(var i = startRow; i >= endRow; i--){
24471                 this.selectRow(i, true);
24472             }
24473         }
24474     },
24475
24476     /**
24477      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24478      * @param {Number} startRow The index of the first row in the range
24479      * @param {Number} endRow The index of the last row in the range
24480      */
24481     deselectRange : function(startRow, endRow, preventViewNotify){
24482         if(this.locked) {
24483             return;
24484         }
24485         for(var i = startRow; i <= endRow; i++){
24486             this.deselectRow(i, preventViewNotify);
24487         }
24488     },
24489
24490     /**
24491      * Selects a row.
24492      * @param {Number} row The index of the row to select
24493      * @param {Boolean} keepExisting (optional) True to keep existing selections
24494      */
24495     selectRow : function(index, keepExisting, preventViewNotify)
24496     {
24497             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24498             return;
24499         }
24500         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24501             if(!keepExisting || this.singleSelect){
24502                 this.clearSelections();
24503             }
24504             
24505             var r = this.grid.store.getAt(index);
24506             //console.log('selectRow - record id :' + r.id);
24507             
24508             this.selections.add(r);
24509             this.last = this.lastActive = index;
24510             if(!preventViewNotify){
24511                 var proxy = new Roo.Element(
24512                                 this.grid.getRowDom(index)
24513                 );
24514                 proxy.addClass('bg-info info');
24515             }
24516             this.fireEvent("rowselect", this, index, r);
24517             this.fireEvent("selectionchange", this);
24518         }
24519     },
24520
24521     /**
24522      * Deselects a row.
24523      * @param {Number} row The index of the row to deselect
24524      */
24525     deselectRow : function(index, preventViewNotify)
24526     {
24527         if(this.locked) {
24528             return;
24529         }
24530         if(this.last == index){
24531             this.last = false;
24532         }
24533         if(this.lastActive == index){
24534             this.lastActive = false;
24535         }
24536         
24537         var r = this.grid.store.getAt(index);
24538         if (!r) {
24539             return;
24540         }
24541         
24542         this.selections.remove(r);
24543         //.console.log('deselectRow - record id :' + r.id);
24544         if(!preventViewNotify){
24545         
24546             var proxy = new Roo.Element(
24547                 this.grid.getRowDom(index)
24548             );
24549             proxy.removeClass('bg-info info');
24550         }
24551         this.fireEvent("rowdeselect", this, index);
24552         this.fireEvent("selectionchange", this);
24553     },
24554
24555     // private
24556     restoreLast : function(){
24557         if(this._last){
24558             this.last = this._last;
24559         }
24560     },
24561
24562     // private
24563     acceptsNav : function(row, col, cm){
24564         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24565     },
24566
24567     // private
24568     onEditorKey : function(field, e){
24569         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24570         if(k == e.TAB){
24571             e.stopEvent();
24572             ed.completeEdit();
24573             if(e.shiftKey){
24574                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24575             }else{
24576                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24577             }
24578         }else if(k == e.ENTER && !e.ctrlKey){
24579             e.stopEvent();
24580             ed.completeEdit();
24581             if(e.shiftKey){
24582                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24583             }else{
24584                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24585             }
24586         }else if(k == e.ESC){
24587             ed.cancelEdit();
24588         }
24589         if(newCell){
24590             g.startEditing(newCell[0], newCell[1]);
24591         }
24592     }
24593 });
24594 /*
24595  * Based on:
24596  * Ext JS Library 1.1.1
24597  * Copyright(c) 2006-2007, Ext JS, LLC.
24598  *
24599  * Originally Released Under LGPL - original licence link has changed is not relivant.
24600  *
24601  * Fork - LGPL
24602  * <script type="text/javascript">
24603  */
24604  
24605 /**
24606  * @class Roo.bootstrap.PagingToolbar
24607  * @extends Roo.bootstrap.NavSimplebar
24608  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24609  * @constructor
24610  * Create a new PagingToolbar
24611  * @param {Object} config The config object
24612  * @param {Roo.data.Store} store
24613  */
24614 Roo.bootstrap.PagingToolbar = function(config)
24615 {
24616     // old args format still supported... - xtype is prefered..
24617         // created from xtype...
24618     
24619     this.ds = config.dataSource;
24620     
24621     if (config.store && !this.ds) {
24622         this.store= Roo.factory(config.store, Roo.data);
24623         this.ds = this.store;
24624         this.ds.xmodule = this.xmodule || false;
24625     }
24626     
24627     this.toolbarItems = [];
24628     if (config.items) {
24629         this.toolbarItems = config.items;
24630     }
24631     
24632     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24633     
24634     this.cursor = 0;
24635     
24636     if (this.ds) { 
24637         this.bind(this.ds);
24638     }
24639     
24640     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24641     
24642 };
24643
24644 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24645     /**
24646      * @cfg {Roo.data.Store} dataSource
24647      * The underlying data store providing the paged data
24648      */
24649     /**
24650      * @cfg {String/HTMLElement/Element} container
24651      * container The id or element that will contain the toolbar
24652      */
24653     /**
24654      * @cfg {Boolean} displayInfo
24655      * True to display the displayMsg (defaults to false)
24656      */
24657     /**
24658      * @cfg {Number} pageSize
24659      * The number of records to display per page (defaults to 20)
24660      */
24661     pageSize: 20,
24662     /**
24663      * @cfg {String} displayMsg
24664      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24665      */
24666     displayMsg : 'Displaying {0} - {1} of {2}',
24667     /**
24668      * @cfg {String} emptyMsg
24669      * The message to display when no records are found (defaults to "No data to display")
24670      */
24671     emptyMsg : 'No data to display',
24672     /**
24673      * Customizable piece of the default paging text (defaults to "Page")
24674      * @type String
24675      */
24676     beforePageText : "Page",
24677     /**
24678      * Customizable piece of the default paging text (defaults to "of %0")
24679      * @type String
24680      */
24681     afterPageText : "of {0}",
24682     /**
24683      * Customizable piece of the default paging text (defaults to "First Page")
24684      * @type String
24685      */
24686     firstText : "First Page",
24687     /**
24688      * Customizable piece of the default paging text (defaults to "Previous Page")
24689      * @type String
24690      */
24691     prevText : "Previous Page",
24692     /**
24693      * Customizable piece of the default paging text (defaults to "Next Page")
24694      * @type String
24695      */
24696     nextText : "Next Page",
24697     /**
24698      * Customizable piece of the default paging text (defaults to "Last Page")
24699      * @type String
24700      */
24701     lastText : "Last Page",
24702     /**
24703      * Customizable piece of the default paging text (defaults to "Refresh")
24704      * @type String
24705      */
24706     refreshText : "Refresh",
24707
24708     buttons : false,
24709     // private
24710     onRender : function(ct, position) 
24711     {
24712         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24713         this.navgroup.parentId = this.id;
24714         this.navgroup.onRender(this.el, null);
24715         // add the buttons to the navgroup
24716         
24717         if(this.displayInfo){
24718             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24719             this.displayEl = this.el.select('.x-paging-info', true).first();
24720 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24721 //            this.displayEl = navel.el.select('span',true).first();
24722         }
24723         
24724         var _this = this;
24725         
24726         if(this.buttons){
24727             Roo.each(_this.buttons, function(e){ // this might need to use render????
24728                Roo.factory(e).render(_this.el);
24729             });
24730         }
24731             
24732         Roo.each(_this.toolbarItems, function(e) {
24733             _this.navgroup.addItem(e);
24734         });
24735         
24736         
24737         this.first = this.navgroup.addItem({
24738             tooltip: this.firstText,
24739             cls: "prev",
24740             icon : 'fa fa-backward',
24741             disabled: true,
24742             preventDefault: true,
24743             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24744         });
24745         
24746         this.prev =  this.navgroup.addItem({
24747             tooltip: this.prevText,
24748             cls: "prev",
24749             icon : 'fa fa-step-backward',
24750             disabled: true,
24751             preventDefault: true,
24752             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24753         });
24754     //this.addSeparator();
24755         
24756         
24757         var field = this.navgroup.addItem( {
24758             tagtype : 'span',
24759             cls : 'x-paging-position',
24760             
24761             html : this.beforePageText  +
24762                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24763                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24764          } ); //?? escaped?
24765         
24766         this.field = field.el.select('input', true).first();
24767         this.field.on("keydown", this.onPagingKeydown, this);
24768         this.field.on("focus", function(){this.dom.select();});
24769     
24770     
24771         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24772         //this.field.setHeight(18);
24773         //this.addSeparator();
24774         this.next = this.navgroup.addItem({
24775             tooltip: this.nextText,
24776             cls: "next",
24777             html : ' <i class="fa fa-step-forward">',
24778             disabled: true,
24779             preventDefault: true,
24780             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24781         });
24782         this.last = this.navgroup.addItem({
24783             tooltip: this.lastText,
24784             icon : 'fa fa-forward',
24785             cls: "next",
24786             disabled: true,
24787             preventDefault: true,
24788             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24789         });
24790     //this.addSeparator();
24791         this.loading = this.navgroup.addItem({
24792             tooltip: this.refreshText,
24793             icon: 'fa fa-refresh',
24794             preventDefault: true,
24795             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24796         });
24797         
24798     },
24799
24800     // private
24801     updateInfo : function(){
24802         if(this.displayEl){
24803             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24804             var msg = count == 0 ?
24805                 this.emptyMsg :
24806                 String.format(
24807                     this.displayMsg,
24808                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24809                 );
24810             this.displayEl.update(msg);
24811         }
24812     },
24813
24814     // private
24815     onLoad : function(ds, r, o)
24816     {
24817         this.cursor = o.params.start ? o.params.start : 0;
24818         
24819         var d = this.getPageData(),
24820             ap = d.activePage,
24821             ps = d.pages;
24822         
24823         
24824         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24825         this.field.dom.value = ap;
24826         this.first.setDisabled(ap == 1);
24827         this.prev.setDisabled(ap == 1);
24828         this.next.setDisabled(ap == ps);
24829         this.last.setDisabled(ap == ps);
24830         this.loading.enable();
24831         this.updateInfo();
24832     },
24833
24834     // private
24835     getPageData : function(){
24836         var total = this.ds.getTotalCount();
24837         return {
24838             total : total,
24839             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24840             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24841         };
24842     },
24843
24844     // private
24845     onLoadError : function(){
24846         this.loading.enable();
24847     },
24848
24849     // private
24850     onPagingKeydown : function(e){
24851         var k = e.getKey();
24852         var d = this.getPageData();
24853         if(k == e.RETURN){
24854             var v = this.field.dom.value, pageNum;
24855             if(!v || isNaN(pageNum = parseInt(v, 10))){
24856                 this.field.dom.value = d.activePage;
24857                 return;
24858             }
24859             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24861             e.stopEvent();
24862         }
24863         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))
24864         {
24865           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24866           this.field.dom.value = pageNum;
24867           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24868           e.stopEvent();
24869         }
24870         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24871         {
24872           var v = this.field.dom.value, pageNum; 
24873           var increment = (e.shiftKey) ? 10 : 1;
24874           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24875                 increment *= -1;
24876           }
24877           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24878             this.field.dom.value = d.activePage;
24879             return;
24880           }
24881           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24882           {
24883             this.field.dom.value = parseInt(v, 10) + increment;
24884             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24885             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24886           }
24887           e.stopEvent();
24888         }
24889     },
24890
24891     // private
24892     beforeLoad : function(){
24893         if(this.loading){
24894             this.loading.disable();
24895         }
24896     },
24897
24898     // private
24899     onClick : function(which){
24900         
24901         var ds = this.ds;
24902         if (!ds) {
24903             return;
24904         }
24905         
24906         switch(which){
24907             case "first":
24908                 ds.load({params:{start: 0, limit: this.pageSize}});
24909             break;
24910             case "prev":
24911                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24912             break;
24913             case "next":
24914                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24915             break;
24916             case "last":
24917                 var total = ds.getTotalCount();
24918                 var extra = total % this.pageSize;
24919                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24920                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24921             break;
24922             case "refresh":
24923                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24924             break;
24925         }
24926     },
24927
24928     /**
24929      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24930      * @param {Roo.data.Store} store The data store to unbind
24931      */
24932     unbind : function(ds){
24933         ds.un("beforeload", this.beforeLoad, this);
24934         ds.un("load", this.onLoad, this);
24935         ds.un("loadexception", this.onLoadError, this);
24936         ds.un("remove", this.updateInfo, this);
24937         ds.un("add", this.updateInfo, this);
24938         this.ds = undefined;
24939     },
24940
24941     /**
24942      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24943      * @param {Roo.data.Store} store The data store to bind
24944      */
24945     bind : function(ds){
24946         ds.on("beforeload", this.beforeLoad, this);
24947         ds.on("load", this.onLoad, this);
24948         ds.on("loadexception", this.onLoadError, this);
24949         ds.on("remove", this.updateInfo, this);
24950         ds.on("add", this.updateInfo, this);
24951         this.ds = ds;
24952     }
24953 });/*
24954  * - LGPL
24955  *
24956  * element
24957  * 
24958  */
24959
24960 /**
24961  * @class Roo.bootstrap.MessageBar
24962  * @extends Roo.bootstrap.Component
24963  * Bootstrap MessageBar class
24964  * @cfg {String} html contents of the MessageBar
24965  * @cfg {String} weight (info | success | warning | danger) default info
24966  * @cfg {String} beforeClass insert the bar before the given class
24967  * @cfg {Boolean} closable (true | false) default false
24968  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24969  * 
24970  * @constructor
24971  * Create a new Element
24972  * @param {Object} config The config object
24973  */
24974
24975 Roo.bootstrap.MessageBar = function(config){
24976     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24977 };
24978
24979 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24980     
24981     html: '',
24982     weight: 'info',
24983     closable: false,
24984     fixed: false,
24985     beforeClass: 'bootstrap-sticky-wrap',
24986     
24987     getAutoCreate : function(){
24988         
24989         var cfg = {
24990             tag: 'div',
24991             cls: 'alert alert-dismissable alert-' + this.weight,
24992             cn: [
24993                 {
24994                     tag: 'span',
24995                     cls: 'message',
24996                     html: this.html || ''
24997                 }
24998             ]
24999         };
25000         
25001         if(this.fixed){
25002             cfg.cls += ' alert-messages-fixed';
25003         }
25004         
25005         if(this.closable){
25006             cfg.cn.push({
25007                 tag: 'button',
25008                 cls: 'close',
25009                 html: 'x'
25010             });
25011         }
25012         
25013         return cfg;
25014     },
25015     
25016     onRender : function(ct, position)
25017     {
25018         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25019         
25020         if(!this.el){
25021             var cfg = Roo.apply({},  this.getAutoCreate());
25022             cfg.id = Roo.id();
25023             
25024             if (this.cls) {
25025                 cfg.cls += ' ' + this.cls;
25026             }
25027             if (this.style) {
25028                 cfg.style = this.style;
25029             }
25030             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25031             
25032             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25033         }
25034         
25035         this.el.select('>button.close').on('click', this.hide, this);
25036         
25037     },
25038     
25039     show : function()
25040     {
25041         if (!this.rendered) {
25042             this.render();
25043         }
25044         
25045         this.el.show();
25046         
25047         this.fireEvent('show', this);
25048         
25049     },
25050     
25051     hide : function()
25052     {
25053         if (!this.rendered) {
25054             this.render();
25055         }
25056         
25057         this.el.hide();
25058         
25059         this.fireEvent('hide', this);
25060     },
25061     
25062     update : function()
25063     {
25064 //        var e = this.el.dom.firstChild;
25065 //        
25066 //        if(this.closable){
25067 //            e = e.nextSibling;
25068 //        }
25069 //        
25070 //        e.data = this.html || '';
25071
25072         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25073     }
25074    
25075 });
25076
25077  
25078
25079      /*
25080  * - LGPL
25081  *
25082  * Graph
25083  * 
25084  */
25085
25086
25087 /**
25088  * @class Roo.bootstrap.Graph
25089  * @extends Roo.bootstrap.Component
25090  * Bootstrap Graph class
25091 > Prameters
25092  -sm {number} sm 4
25093  -md {number} md 5
25094  @cfg {String} graphtype  bar | vbar | pie
25095  @cfg {number} g_x coodinator | centre x (pie)
25096  @cfg {number} g_y coodinator | centre y (pie)
25097  @cfg {number} g_r radius (pie)
25098  @cfg {number} g_height height of the chart (respected by all elements in the set)
25099  @cfg {number} g_width width of the chart (respected by all elements in the set)
25100  @cfg {Object} title The title of the chart
25101     
25102  -{Array}  values
25103  -opts (object) options for the chart 
25104      o {
25105      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25106      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25107      o vgutter (number)
25108      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.
25109      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25110      o to
25111      o stretch (boolean)
25112      o }
25113  -opts (object) options for the pie
25114      o{
25115      o cut
25116      o startAngle (number)
25117      o endAngle (number)
25118      } 
25119  *
25120  * @constructor
25121  * Create a new Input
25122  * @param {Object} config The config object
25123  */
25124
25125 Roo.bootstrap.Graph = function(config){
25126     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25127     
25128     this.addEvents({
25129         // img events
25130         /**
25131          * @event click
25132          * The img click event for the img.
25133          * @param {Roo.EventObject} e
25134          */
25135         "click" : true
25136     });
25137 };
25138
25139 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25140     
25141     sm: 4,
25142     md: 5,
25143     graphtype: 'bar',
25144     g_height: 250,
25145     g_width: 400,
25146     g_x: 50,
25147     g_y: 50,
25148     g_r: 30,
25149     opts:{
25150         //g_colors: this.colors,
25151         g_type: 'soft',
25152         g_gutter: '20%'
25153
25154     },
25155     title : false,
25156
25157     getAutoCreate : function(){
25158         
25159         var cfg = {
25160             tag: 'div',
25161             html : null
25162         };
25163         
25164         
25165         return  cfg;
25166     },
25167
25168     onRender : function(ct,position){
25169         
25170         
25171         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25172         
25173         if (typeof(Raphael) == 'undefined') {
25174             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25175             return;
25176         }
25177         
25178         this.raphael = Raphael(this.el.dom);
25179         
25180                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25181                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25182                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25183                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25184                 /*
25185                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25186                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25187                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25188                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25189                 
25190                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25191                 r.barchart(330, 10, 300, 220, data1);
25192                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25193                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25194                 */
25195                 
25196                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25197                 // r.barchart(30, 30, 560, 250,  xdata, {
25198                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25199                 //     axis : "0 0 1 1",
25200                 //     axisxlabels :  xdata
25201                 //     //yvalues : cols,
25202                    
25203                 // });
25204 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25205 //        
25206 //        this.load(null,xdata,{
25207 //                axis : "0 0 1 1",
25208 //                axisxlabels :  xdata
25209 //                });
25210
25211     },
25212
25213     load : function(graphtype,xdata,opts)
25214     {
25215         this.raphael.clear();
25216         if(!graphtype) {
25217             graphtype = this.graphtype;
25218         }
25219         if(!opts){
25220             opts = this.opts;
25221         }
25222         var r = this.raphael,
25223             fin = function () {
25224                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25225             },
25226             fout = function () {
25227                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25228             },
25229             pfin = function() {
25230                 this.sector.stop();
25231                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25232
25233                 if (this.label) {
25234                     this.label[0].stop();
25235                     this.label[0].attr({ r: 7.5 });
25236                     this.label[1].attr({ "font-weight": 800 });
25237                 }
25238             },
25239             pfout = function() {
25240                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25241
25242                 if (this.label) {
25243                     this.label[0].animate({ r: 5 }, 500, "bounce");
25244                     this.label[1].attr({ "font-weight": 400 });
25245                 }
25246             };
25247
25248         switch(graphtype){
25249             case 'bar':
25250                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25251                 break;
25252             case 'hbar':
25253                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25254                 break;
25255             case 'pie':
25256 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25257 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25258 //            
25259                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25260                 
25261                 break;
25262
25263         }
25264         
25265         if(this.title){
25266             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25267         }
25268         
25269     },
25270     
25271     setTitle: function(o)
25272     {
25273         this.title = o;
25274     },
25275     
25276     initEvents: function() {
25277         
25278         if(!this.href){
25279             this.el.on('click', this.onClick, this);
25280         }
25281     },
25282     
25283     onClick : function(e)
25284     {
25285         Roo.log('img onclick');
25286         this.fireEvent('click', this, e);
25287     }
25288    
25289 });
25290
25291  
25292 /*
25293  * - LGPL
25294  *
25295  * numberBox
25296  * 
25297  */
25298 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25299
25300 /**
25301  * @class Roo.bootstrap.dash.NumberBox
25302  * @extends Roo.bootstrap.Component
25303  * Bootstrap NumberBox class
25304  * @cfg {String} headline Box headline
25305  * @cfg {String} content Box content
25306  * @cfg {String} icon Box icon
25307  * @cfg {String} footer Footer text
25308  * @cfg {String} fhref Footer href
25309  * 
25310  * @constructor
25311  * Create a new NumberBox
25312  * @param {Object} config The config object
25313  */
25314
25315
25316 Roo.bootstrap.dash.NumberBox = function(config){
25317     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25318     
25319 };
25320
25321 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25322     
25323     headline : '',
25324     content : '',
25325     icon : '',
25326     footer : '',
25327     fhref : '',
25328     ficon : '',
25329     
25330     getAutoCreate : function(){
25331         
25332         var cfg = {
25333             tag : 'div',
25334             cls : 'small-box ',
25335             cn : [
25336                 {
25337                     tag : 'div',
25338                     cls : 'inner',
25339                     cn :[
25340                         {
25341                             tag : 'h3',
25342                             cls : 'roo-headline',
25343                             html : this.headline
25344                         },
25345                         {
25346                             tag : 'p',
25347                             cls : 'roo-content',
25348                             html : this.content
25349                         }
25350                     ]
25351                 }
25352             ]
25353         };
25354         
25355         if(this.icon){
25356             cfg.cn.push({
25357                 tag : 'div',
25358                 cls : 'icon',
25359                 cn :[
25360                     {
25361                         tag : 'i',
25362                         cls : 'ion ' + this.icon
25363                     }
25364                 ]
25365             });
25366         }
25367         
25368         if(this.footer){
25369             var footer = {
25370                 tag : 'a',
25371                 cls : 'small-box-footer',
25372                 href : this.fhref || '#',
25373                 html : this.footer
25374             };
25375             
25376             cfg.cn.push(footer);
25377             
25378         }
25379         
25380         return  cfg;
25381     },
25382
25383     onRender : function(ct,position){
25384         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25385
25386
25387        
25388                 
25389     },
25390
25391     setHeadline: function (value)
25392     {
25393         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25394     },
25395     
25396     setFooter: function (value, href)
25397     {
25398         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25399         
25400         if(href){
25401             this.el.select('a.small-box-footer',true).first().attr('href', href);
25402         }
25403         
25404     },
25405
25406     setContent: function (value)
25407     {
25408         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25409     },
25410
25411     initEvents: function() 
25412     {   
25413         
25414     }
25415     
25416 });
25417
25418  
25419 /*
25420  * - LGPL
25421  *
25422  * TabBox
25423  * 
25424  */
25425 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25426
25427 /**
25428  * @class Roo.bootstrap.dash.TabBox
25429  * @extends Roo.bootstrap.Component
25430  * Bootstrap TabBox class
25431  * @cfg {String} title Title of the TabBox
25432  * @cfg {String} icon Icon of the TabBox
25433  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25434  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25435  * 
25436  * @constructor
25437  * Create a new TabBox
25438  * @param {Object} config The config object
25439  */
25440
25441
25442 Roo.bootstrap.dash.TabBox = function(config){
25443     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25444     this.addEvents({
25445         // raw events
25446         /**
25447          * @event addpane
25448          * When a pane is added
25449          * @param {Roo.bootstrap.dash.TabPane} pane
25450          */
25451         "addpane" : true,
25452         /**
25453          * @event activatepane
25454          * When a pane is activated
25455          * @param {Roo.bootstrap.dash.TabPane} pane
25456          */
25457         "activatepane" : true
25458         
25459          
25460     });
25461     
25462     this.panes = [];
25463 };
25464
25465 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25466
25467     title : '',
25468     icon : false,
25469     showtabs : true,
25470     tabScrollable : false,
25471     
25472     getChildContainer : function()
25473     {
25474         return this.el.select('.tab-content', true).first();
25475     },
25476     
25477     getAutoCreate : function(){
25478         
25479         var header = {
25480             tag: 'li',
25481             cls: 'pull-left header',
25482             html: this.title,
25483             cn : []
25484         };
25485         
25486         if(this.icon){
25487             header.cn.push({
25488                 tag: 'i',
25489                 cls: 'fa ' + this.icon
25490             });
25491         }
25492         
25493         var h = {
25494             tag: 'ul',
25495             cls: 'nav nav-tabs pull-right',
25496             cn: [
25497                 header
25498             ]
25499         };
25500         
25501         if(this.tabScrollable){
25502             h = {
25503                 tag: 'div',
25504                 cls: 'tab-header',
25505                 cn: [
25506                     {
25507                         tag: 'ul',
25508                         cls: 'nav nav-tabs pull-right',
25509                         cn: [
25510                             header
25511                         ]
25512                     }
25513                 ]
25514             };
25515         }
25516         
25517         var cfg = {
25518             tag: 'div',
25519             cls: 'nav-tabs-custom',
25520             cn: [
25521                 h,
25522                 {
25523                     tag: 'div',
25524                     cls: 'tab-content no-padding',
25525                     cn: []
25526                 }
25527             ]
25528         };
25529
25530         return  cfg;
25531     },
25532     initEvents : function()
25533     {
25534         //Roo.log('add add pane handler');
25535         this.on('addpane', this.onAddPane, this);
25536     },
25537      /**
25538      * Updates the box title
25539      * @param {String} html to set the title to.
25540      */
25541     setTitle : function(value)
25542     {
25543         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25544     },
25545     onAddPane : function(pane)
25546     {
25547         this.panes.push(pane);
25548         //Roo.log('addpane');
25549         //Roo.log(pane);
25550         // tabs are rendere left to right..
25551         if(!this.showtabs){
25552             return;
25553         }
25554         
25555         var ctr = this.el.select('.nav-tabs', true).first();
25556          
25557          
25558         var existing = ctr.select('.nav-tab',true);
25559         var qty = existing.getCount();;
25560         
25561         
25562         var tab = ctr.createChild({
25563             tag : 'li',
25564             cls : 'nav-tab' + (qty ? '' : ' active'),
25565             cn : [
25566                 {
25567                     tag : 'a',
25568                     href:'#',
25569                     html : pane.title
25570                 }
25571             ]
25572         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25573         pane.tab = tab;
25574         
25575         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25576         if (!qty) {
25577             pane.el.addClass('active');
25578         }
25579         
25580                 
25581     },
25582     onTabClick : function(ev,un,ob,pane)
25583     {
25584         //Roo.log('tab - prev default');
25585         ev.preventDefault();
25586         
25587         
25588         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25589         pane.tab.addClass('active');
25590         //Roo.log(pane.title);
25591         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25592         // technically we should have a deactivate event.. but maybe add later.
25593         // and it should not de-activate the selected tab...
25594         this.fireEvent('activatepane', pane);
25595         pane.el.addClass('active');
25596         pane.fireEvent('activate');
25597         
25598         
25599     },
25600     
25601     getActivePane : function()
25602     {
25603         var r = false;
25604         Roo.each(this.panes, function(p) {
25605             if(p.el.hasClass('active')){
25606                 r = p;
25607                 return false;
25608             }
25609             
25610             return;
25611         });
25612         
25613         return r;
25614     }
25615     
25616     
25617 });
25618
25619  
25620 /*
25621  * - LGPL
25622  *
25623  * Tab pane
25624  * 
25625  */
25626 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25627 /**
25628  * @class Roo.bootstrap.TabPane
25629  * @extends Roo.bootstrap.Component
25630  * Bootstrap TabPane class
25631  * @cfg {Boolean} active (false | true) Default false
25632  * @cfg {String} title title of panel
25633
25634  * 
25635  * @constructor
25636  * Create a new TabPane
25637  * @param {Object} config The config object
25638  */
25639
25640 Roo.bootstrap.dash.TabPane = function(config){
25641     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25642     
25643     this.addEvents({
25644         // raw events
25645         /**
25646          * @event activate
25647          * When a pane is activated
25648          * @param {Roo.bootstrap.dash.TabPane} pane
25649          */
25650         "activate" : true
25651          
25652     });
25653 };
25654
25655 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25656     
25657     active : false,
25658     title : '',
25659     
25660     // the tabBox that this is attached to.
25661     tab : false,
25662      
25663     getAutoCreate : function() 
25664     {
25665         var cfg = {
25666             tag: 'div',
25667             cls: 'tab-pane'
25668         };
25669         
25670         if(this.active){
25671             cfg.cls += ' active';
25672         }
25673         
25674         return cfg;
25675     },
25676     initEvents  : function()
25677     {
25678         //Roo.log('trigger add pane handler');
25679         this.parent().fireEvent('addpane', this)
25680     },
25681     
25682      /**
25683      * Updates the tab title 
25684      * @param {String} html to set the title to.
25685      */
25686     setTitle: function(str)
25687     {
25688         if (!this.tab) {
25689             return;
25690         }
25691         this.title = str;
25692         this.tab.select('a', true).first().dom.innerHTML = str;
25693         
25694     }
25695     
25696     
25697     
25698 });
25699
25700  
25701
25702
25703  /*
25704  * - LGPL
25705  *
25706  * menu
25707  * 
25708  */
25709 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25710
25711 /**
25712  * @class Roo.bootstrap.menu.Menu
25713  * @extends Roo.bootstrap.Component
25714  * Bootstrap Menu class - container for Menu
25715  * @cfg {String} html Text of the menu
25716  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25717  * @cfg {String} icon Font awesome icon
25718  * @cfg {String} pos Menu align to (top | bottom) default bottom
25719  * 
25720  * 
25721  * @constructor
25722  * Create a new Menu
25723  * @param {Object} config The config object
25724  */
25725
25726
25727 Roo.bootstrap.menu.Menu = function(config){
25728     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25729     
25730     this.addEvents({
25731         /**
25732          * @event beforeshow
25733          * Fires before this menu is displayed
25734          * @param {Roo.bootstrap.menu.Menu} this
25735          */
25736         beforeshow : true,
25737         /**
25738          * @event beforehide
25739          * Fires before this menu is hidden
25740          * @param {Roo.bootstrap.menu.Menu} this
25741          */
25742         beforehide : true,
25743         /**
25744          * @event show
25745          * Fires after this menu is displayed
25746          * @param {Roo.bootstrap.menu.Menu} this
25747          */
25748         show : true,
25749         /**
25750          * @event hide
25751          * Fires after this menu is hidden
25752          * @param {Roo.bootstrap.menu.Menu} this
25753          */
25754         hide : true,
25755         /**
25756          * @event click
25757          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25758          * @param {Roo.bootstrap.menu.Menu} this
25759          * @param {Roo.EventObject} e
25760          */
25761         click : true
25762     });
25763     
25764 };
25765
25766 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25767     
25768     submenu : false,
25769     html : '',
25770     weight : 'default',
25771     icon : false,
25772     pos : 'bottom',
25773     
25774     
25775     getChildContainer : function() {
25776         if(this.isSubMenu){
25777             return this.el;
25778         }
25779         
25780         return this.el.select('ul.dropdown-menu', true).first();  
25781     },
25782     
25783     getAutoCreate : function()
25784     {
25785         var text = [
25786             {
25787                 tag : 'span',
25788                 cls : 'roo-menu-text',
25789                 html : this.html
25790             }
25791         ];
25792         
25793         if(this.icon){
25794             text.unshift({
25795                 tag : 'i',
25796                 cls : 'fa ' + this.icon
25797             })
25798         }
25799         
25800         
25801         var cfg = {
25802             tag : 'div',
25803             cls : 'btn-group',
25804             cn : [
25805                 {
25806                     tag : 'button',
25807                     cls : 'dropdown-button btn btn-' + this.weight,
25808                     cn : text
25809                 },
25810                 {
25811                     tag : 'button',
25812                     cls : 'dropdown-toggle btn btn-' + this.weight,
25813                     cn : [
25814                         {
25815                             tag : 'span',
25816                             cls : 'caret'
25817                         }
25818                     ]
25819                 },
25820                 {
25821                     tag : 'ul',
25822                     cls : 'dropdown-menu'
25823                 }
25824             ]
25825             
25826         };
25827         
25828         if(this.pos == 'top'){
25829             cfg.cls += ' dropup';
25830         }
25831         
25832         if(this.isSubMenu){
25833             cfg = {
25834                 tag : 'ul',
25835                 cls : 'dropdown-menu'
25836             }
25837         }
25838         
25839         return cfg;
25840     },
25841     
25842     onRender : function(ct, position)
25843     {
25844         this.isSubMenu = ct.hasClass('dropdown-submenu');
25845         
25846         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25847     },
25848     
25849     initEvents : function() 
25850     {
25851         if(this.isSubMenu){
25852             return;
25853         }
25854         
25855         this.hidden = true;
25856         
25857         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25858         this.triggerEl.on('click', this.onTriggerPress, this);
25859         
25860         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25861         this.buttonEl.on('click', this.onClick, this);
25862         
25863     },
25864     
25865     list : function()
25866     {
25867         if(this.isSubMenu){
25868             return this.el;
25869         }
25870         
25871         return this.el.select('ul.dropdown-menu', true).first();
25872     },
25873     
25874     onClick : function(e)
25875     {
25876         this.fireEvent("click", this, e);
25877     },
25878     
25879     onTriggerPress  : function(e)
25880     {   
25881         if (this.isVisible()) {
25882             this.hide();
25883         } else {
25884             this.show();
25885         }
25886     },
25887     
25888     isVisible : function(){
25889         return !this.hidden;
25890     },
25891     
25892     show : function()
25893     {
25894         this.fireEvent("beforeshow", this);
25895         
25896         this.hidden = false;
25897         this.el.addClass('open');
25898         
25899         Roo.get(document).on("mouseup", this.onMouseUp, this);
25900         
25901         this.fireEvent("show", this);
25902         
25903         
25904     },
25905     
25906     hide : function()
25907     {
25908         this.fireEvent("beforehide", this);
25909         
25910         this.hidden = true;
25911         this.el.removeClass('open');
25912         
25913         Roo.get(document).un("mouseup", this.onMouseUp);
25914         
25915         this.fireEvent("hide", this);
25916     },
25917     
25918     onMouseUp : function()
25919     {
25920         this.hide();
25921     }
25922     
25923 });
25924
25925  
25926  /*
25927  * - LGPL
25928  *
25929  * menu item
25930  * 
25931  */
25932 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25933
25934 /**
25935  * @class Roo.bootstrap.menu.Item
25936  * @extends Roo.bootstrap.Component
25937  * Bootstrap MenuItem class
25938  * @cfg {Boolean} submenu (true | false) default false
25939  * @cfg {String} html text of the item
25940  * @cfg {String} href the link
25941  * @cfg {Boolean} disable (true | false) default false
25942  * @cfg {Boolean} preventDefault (true | false) default true
25943  * @cfg {String} icon Font awesome icon
25944  * @cfg {String} pos Submenu align to (left | right) default right 
25945  * 
25946  * 
25947  * @constructor
25948  * Create a new Item
25949  * @param {Object} config The config object
25950  */
25951
25952
25953 Roo.bootstrap.menu.Item = function(config){
25954     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25955     this.addEvents({
25956         /**
25957          * @event mouseover
25958          * Fires when the mouse is hovering over this menu
25959          * @param {Roo.bootstrap.menu.Item} this
25960          * @param {Roo.EventObject} e
25961          */
25962         mouseover : true,
25963         /**
25964          * @event mouseout
25965          * Fires when the mouse exits this menu
25966          * @param {Roo.bootstrap.menu.Item} this
25967          * @param {Roo.EventObject} e
25968          */
25969         mouseout : true,
25970         // raw events
25971         /**
25972          * @event click
25973          * The raw click event for the entire grid.
25974          * @param {Roo.EventObject} e
25975          */
25976         click : true
25977     });
25978 };
25979
25980 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25981     
25982     submenu : false,
25983     href : '',
25984     html : '',
25985     preventDefault: true,
25986     disable : false,
25987     icon : false,
25988     pos : 'right',
25989     
25990     getAutoCreate : function()
25991     {
25992         var text = [
25993             {
25994                 tag : 'span',
25995                 cls : 'roo-menu-item-text',
25996                 html : this.html
25997             }
25998         ];
25999         
26000         if(this.icon){
26001             text.unshift({
26002                 tag : 'i',
26003                 cls : 'fa ' + this.icon
26004             })
26005         }
26006         
26007         var cfg = {
26008             tag : 'li',
26009             cn : [
26010                 {
26011                     tag : 'a',
26012                     href : this.href || '#',
26013                     cn : text
26014                 }
26015             ]
26016         };
26017         
26018         if(this.disable){
26019             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26020         }
26021         
26022         if(this.submenu){
26023             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26024             
26025             if(this.pos == 'left'){
26026                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26027             }
26028         }
26029         
26030         return cfg;
26031     },
26032     
26033     initEvents : function() 
26034     {
26035         this.el.on('mouseover', this.onMouseOver, this);
26036         this.el.on('mouseout', this.onMouseOut, this);
26037         
26038         this.el.select('a', true).first().on('click', this.onClick, this);
26039         
26040     },
26041     
26042     onClick : function(e)
26043     {
26044         if(this.preventDefault){
26045             e.preventDefault();
26046         }
26047         
26048         this.fireEvent("click", this, e);
26049     },
26050     
26051     onMouseOver : function(e)
26052     {
26053         if(this.submenu && this.pos == 'left'){
26054             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26055         }
26056         
26057         this.fireEvent("mouseover", this, e);
26058     },
26059     
26060     onMouseOut : function(e)
26061     {
26062         this.fireEvent("mouseout", this, e);
26063     }
26064 });
26065
26066  
26067
26068  /*
26069  * - LGPL
26070  *
26071  * menu separator
26072  * 
26073  */
26074 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26075
26076 /**
26077  * @class Roo.bootstrap.menu.Separator
26078  * @extends Roo.bootstrap.Component
26079  * Bootstrap Separator class
26080  * 
26081  * @constructor
26082  * Create a new Separator
26083  * @param {Object} config The config object
26084  */
26085
26086
26087 Roo.bootstrap.menu.Separator = function(config){
26088     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26089 };
26090
26091 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26092     
26093     getAutoCreate : function(){
26094         var cfg = {
26095             tag : 'li',
26096             cls: 'divider'
26097         };
26098         
26099         return cfg;
26100     }
26101    
26102 });
26103
26104  
26105
26106  /*
26107  * - LGPL
26108  *
26109  * Tooltip
26110  * 
26111  */
26112
26113 /**
26114  * @class Roo.bootstrap.Tooltip
26115  * Bootstrap Tooltip class
26116  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26117  * to determine which dom element triggers the tooltip.
26118  * 
26119  * It needs to add support for additional attributes like tooltip-position
26120  * 
26121  * @constructor
26122  * Create a new Toolti
26123  * @param {Object} config The config object
26124  */
26125
26126 Roo.bootstrap.Tooltip = function(config){
26127     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26128     
26129     this.alignment = Roo.bootstrap.Tooltip.alignment;
26130     
26131     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26132         this.alignment = config.alignment;
26133     }
26134     
26135 };
26136
26137 Roo.apply(Roo.bootstrap.Tooltip, {
26138     /**
26139      * @function init initialize tooltip monitoring.
26140      * @static
26141      */
26142     currentEl : false,
26143     currentTip : false,
26144     currentRegion : false,
26145     
26146     //  init : delay?
26147     
26148     init : function()
26149     {
26150         Roo.get(document).on('mouseover', this.enter ,this);
26151         Roo.get(document).on('mouseout', this.leave, this);
26152          
26153         
26154         this.currentTip = new Roo.bootstrap.Tooltip();
26155     },
26156     
26157     enter : function(ev)
26158     {
26159         var dom = ev.getTarget();
26160         
26161         //Roo.log(['enter',dom]);
26162         var el = Roo.fly(dom);
26163         if (this.currentEl) {
26164             //Roo.log(dom);
26165             //Roo.log(this.currentEl);
26166             //Roo.log(this.currentEl.contains(dom));
26167             if (this.currentEl == el) {
26168                 return;
26169             }
26170             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26171                 return;
26172             }
26173
26174         }
26175         
26176         if (this.currentTip.el) {
26177             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26178         }    
26179         //Roo.log(ev);
26180         
26181         if(!el || el.dom == document){
26182             return;
26183         }
26184         
26185         var bindEl = el;
26186         
26187         // you can not look for children, as if el is the body.. then everythign is the child..
26188         if (!el.attr('tooltip')) { //
26189             if (!el.select("[tooltip]").elements.length) {
26190                 return;
26191             }
26192             // is the mouse over this child...?
26193             bindEl = el.select("[tooltip]").first();
26194             var xy = ev.getXY();
26195             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26196                 //Roo.log("not in region.");
26197                 return;
26198             }
26199             //Roo.log("child element over..");
26200             
26201         }
26202         this.currentEl = bindEl;
26203         this.currentTip.bind(bindEl);
26204         this.currentRegion = Roo.lib.Region.getRegion(dom);
26205         this.currentTip.enter();
26206         
26207     },
26208     leave : function(ev)
26209     {
26210         var dom = ev.getTarget();
26211         //Roo.log(['leave',dom]);
26212         if (!this.currentEl) {
26213             return;
26214         }
26215         
26216         
26217         if (dom != this.currentEl.dom) {
26218             return;
26219         }
26220         var xy = ev.getXY();
26221         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26222             return;
26223         }
26224         // only activate leave if mouse cursor is outside... bounding box..
26225         
26226         
26227         
26228         
26229         if (this.currentTip) {
26230             this.currentTip.leave();
26231         }
26232         //Roo.log('clear currentEl');
26233         this.currentEl = false;
26234         
26235         
26236     },
26237     alignment : {
26238         'left' : ['r-l', [-2,0], 'right'],
26239         'right' : ['l-r', [2,0], 'left'],
26240         'bottom' : ['t-b', [0,2], 'top'],
26241         'top' : [ 'b-t', [0,-2], 'bottom']
26242     }
26243     
26244 });
26245
26246
26247 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26248     
26249     
26250     bindEl : false,
26251     
26252     delay : null, // can be { show : 300 , hide: 500}
26253     
26254     timeout : null,
26255     
26256     hoverState : null, //???
26257     
26258     placement : 'bottom', 
26259     
26260     alignment : false,
26261     
26262     getAutoCreate : function(){
26263     
26264         var cfg = {
26265            cls : 'tooltip',
26266            role : 'tooltip',
26267            cn : [
26268                 {
26269                     cls : 'tooltip-arrow'
26270                 },
26271                 {
26272                     cls : 'tooltip-inner'
26273                 }
26274            ]
26275         };
26276         
26277         return cfg;
26278     },
26279     bind : function(el)
26280     {
26281         this.bindEl = el;
26282     },
26283       
26284     
26285     enter : function () {
26286        
26287         if (this.timeout != null) {
26288             clearTimeout(this.timeout);
26289         }
26290         
26291         this.hoverState = 'in';
26292          //Roo.log("enter - show");
26293         if (!this.delay || !this.delay.show) {
26294             this.show();
26295             return;
26296         }
26297         var _t = this;
26298         this.timeout = setTimeout(function () {
26299             if (_t.hoverState == 'in') {
26300                 _t.show();
26301             }
26302         }, this.delay.show);
26303     },
26304     leave : function()
26305     {
26306         clearTimeout(this.timeout);
26307     
26308         this.hoverState = 'out';
26309          if (!this.delay || !this.delay.hide) {
26310             this.hide();
26311             return;
26312         }
26313        
26314         var _t = this;
26315         this.timeout = setTimeout(function () {
26316             //Roo.log("leave - timeout");
26317             
26318             if (_t.hoverState == 'out') {
26319                 _t.hide();
26320                 Roo.bootstrap.Tooltip.currentEl = false;
26321             }
26322         }, delay);
26323     },
26324     
26325     show : function (msg)
26326     {
26327         if (!this.el) {
26328             this.render(document.body);
26329         }
26330         // set content.
26331         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26332         
26333         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26334         
26335         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26336         
26337         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26338         
26339         var placement = typeof this.placement == 'function' ?
26340             this.placement.call(this, this.el, on_el) :
26341             this.placement;
26342             
26343         var autoToken = /\s?auto?\s?/i;
26344         var autoPlace = autoToken.test(placement);
26345         if (autoPlace) {
26346             placement = placement.replace(autoToken, '') || 'top';
26347         }
26348         
26349         //this.el.detach()
26350         //this.el.setXY([0,0]);
26351         this.el.show();
26352         //this.el.dom.style.display='block';
26353         
26354         //this.el.appendTo(on_el);
26355         
26356         var p = this.getPosition();
26357         var box = this.el.getBox();
26358         
26359         if (autoPlace) {
26360             // fixme..
26361         }
26362         
26363         var align = this.alignment[placement];
26364         
26365         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26366         
26367         if(placement == 'top' || placement == 'bottom'){
26368             if(xy[0] < 0){
26369                 placement = 'right';
26370             }
26371             
26372             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26373                 placement = 'left';
26374             }
26375             
26376             var scroll = Roo.select('body', true).first().getScroll();
26377             
26378             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26379                 placement = 'top';
26380             }
26381             
26382             align = this.alignment[placement];
26383         }
26384         
26385         this.el.alignTo(this.bindEl, align[0],align[1]);
26386         //var arrow = this.el.select('.arrow',true).first();
26387         //arrow.set(align[2], 
26388         
26389         this.el.addClass(placement);
26390         
26391         this.el.addClass('in fade');
26392         
26393         this.hoverState = null;
26394         
26395         if (this.el.hasClass('fade')) {
26396             // fade it?
26397         }
26398         
26399     },
26400     hide : function()
26401     {
26402          
26403         if (!this.el) {
26404             return;
26405         }
26406         //this.el.setXY([0,0]);
26407         this.el.removeClass('in');
26408         //this.el.hide();
26409         
26410     }
26411     
26412 });
26413  
26414
26415  /*
26416  * - LGPL
26417  *
26418  * Location Picker
26419  * 
26420  */
26421
26422 /**
26423  * @class Roo.bootstrap.LocationPicker
26424  * @extends Roo.bootstrap.Component
26425  * Bootstrap LocationPicker class
26426  * @cfg {Number} latitude Position when init default 0
26427  * @cfg {Number} longitude Position when init default 0
26428  * @cfg {Number} zoom default 15
26429  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26430  * @cfg {Boolean} mapTypeControl default false
26431  * @cfg {Boolean} disableDoubleClickZoom default false
26432  * @cfg {Boolean} scrollwheel default true
26433  * @cfg {Boolean} streetViewControl default false
26434  * @cfg {Number} radius default 0
26435  * @cfg {String} locationName
26436  * @cfg {Boolean} draggable default true
26437  * @cfg {Boolean} enableAutocomplete default false
26438  * @cfg {Boolean} enableReverseGeocode default true
26439  * @cfg {String} markerTitle
26440  * 
26441  * @constructor
26442  * Create a new LocationPicker
26443  * @param {Object} config The config object
26444  */
26445
26446
26447 Roo.bootstrap.LocationPicker = function(config){
26448     
26449     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26450     
26451     this.addEvents({
26452         /**
26453          * @event initial
26454          * Fires when the picker initialized.
26455          * @param {Roo.bootstrap.LocationPicker} this
26456          * @param {Google Location} location
26457          */
26458         initial : true,
26459         /**
26460          * @event positionchanged
26461          * Fires when the picker position changed.
26462          * @param {Roo.bootstrap.LocationPicker} this
26463          * @param {Google Location} location
26464          */
26465         positionchanged : true,
26466         /**
26467          * @event resize
26468          * Fires when the map resize.
26469          * @param {Roo.bootstrap.LocationPicker} this
26470          */
26471         resize : true,
26472         /**
26473          * @event show
26474          * Fires when the map show.
26475          * @param {Roo.bootstrap.LocationPicker} this
26476          */
26477         show : true,
26478         /**
26479          * @event hide
26480          * Fires when the map hide.
26481          * @param {Roo.bootstrap.LocationPicker} this
26482          */
26483         hide : true,
26484         /**
26485          * @event mapClick
26486          * Fires when click the map.
26487          * @param {Roo.bootstrap.LocationPicker} this
26488          * @param {Map event} e
26489          */
26490         mapClick : true,
26491         /**
26492          * @event mapRightClick
26493          * Fires when right click the map.
26494          * @param {Roo.bootstrap.LocationPicker} this
26495          * @param {Map event} e
26496          */
26497         mapRightClick : true,
26498         /**
26499          * @event markerClick
26500          * Fires when click the marker.
26501          * @param {Roo.bootstrap.LocationPicker} this
26502          * @param {Map event} e
26503          */
26504         markerClick : true,
26505         /**
26506          * @event markerRightClick
26507          * Fires when right click the marker.
26508          * @param {Roo.bootstrap.LocationPicker} this
26509          * @param {Map event} e
26510          */
26511         markerRightClick : true,
26512         /**
26513          * @event OverlayViewDraw
26514          * Fires when OverlayView Draw
26515          * @param {Roo.bootstrap.LocationPicker} this
26516          */
26517         OverlayViewDraw : true,
26518         /**
26519          * @event OverlayViewOnAdd
26520          * Fires when OverlayView Draw
26521          * @param {Roo.bootstrap.LocationPicker} this
26522          */
26523         OverlayViewOnAdd : true,
26524         /**
26525          * @event OverlayViewOnRemove
26526          * Fires when OverlayView Draw
26527          * @param {Roo.bootstrap.LocationPicker} this
26528          */
26529         OverlayViewOnRemove : true,
26530         /**
26531          * @event OverlayViewShow
26532          * Fires when OverlayView Draw
26533          * @param {Roo.bootstrap.LocationPicker} this
26534          * @param {Pixel} cpx
26535          */
26536         OverlayViewShow : true,
26537         /**
26538          * @event OverlayViewHide
26539          * Fires when OverlayView Draw
26540          * @param {Roo.bootstrap.LocationPicker} this
26541          */
26542         OverlayViewHide : true,
26543         /**
26544          * @event loadexception
26545          * Fires when load google lib failed.
26546          * @param {Roo.bootstrap.LocationPicker} this
26547          */
26548         loadexception : true
26549     });
26550         
26551 };
26552
26553 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26554     
26555     gMapContext: false,
26556     
26557     latitude: 0,
26558     longitude: 0,
26559     zoom: 15,
26560     mapTypeId: false,
26561     mapTypeControl: false,
26562     disableDoubleClickZoom: false,
26563     scrollwheel: true,
26564     streetViewControl: false,
26565     radius: 0,
26566     locationName: '',
26567     draggable: true,
26568     enableAutocomplete: false,
26569     enableReverseGeocode: true,
26570     markerTitle: '',
26571     
26572     getAutoCreate: function()
26573     {
26574
26575         var cfg = {
26576             tag: 'div',
26577             cls: 'roo-location-picker'
26578         };
26579         
26580         return cfg
26581     },
26582     
26583     initEvents: function(ct, position)
26584     {       
26585         if(!this.el.getWidth() || this.isApplied()){
26586             return;
26587         }
26588         
26589         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26590         
26591         this.initial();
26592     },
26593     
26594     initial: function()
26595     {
26596         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26597             this.fireEvent('loadexception', this);
26598             return;
26599         }
26600         
26601         if(!this.mapTypeId){
26602             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26603         }
26604         
26605         this.gMapContext = this.GMapContext();
26606         
26607         this.initOverlayView();
26608         
26609         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26610         
26611         var _this = this;
26612                 
26613         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26614             _this.setPosition(_this.gMapContext.marker.position);
26615         });
26616         
26617         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26618             _this.fireEvent('mapClick', this, event);
26619             
26620         });
26621
26622         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26623             _this.fireEvent('mapRightClick', this, event);
26624             
26625         });
26626         
26627         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26628             _this.fireEvent('markerClick', this, event);
26629             
26630         });
26631
26632         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26633             _this.fireEvent('markerRightClick', this, event);
26634             
26635         });
26636         
26637         this.setPosition(this.gMapContext.location);
26638         
26639         this.fireEvent('initial', this, this.gMapContext.location);
26640     },
26641     
26642     initOverlayView: function()
26643     {
26644         var _this = this;
26645         
26646         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26647             
26648             draw: function()
26649             {
26650                 _this.fireEvent('OverlayViewDraw', _this);
26651             },
26652             
26653             onAdd: function()
26654             {
26655                 _this.fireEvent('OverlayViewOnAdd', _this);
26656             },
26657             
26658             onRemove: function()
26659             {
26660                 _this.fireEvent('OverlayViewOnRemove', _this);
26661             },
26662             
26663             show: function(cpx)
26664             {
26665                 _this.fireEvent('OverlayViewShow', _this, cpx);
26666             },
26667             
26668             hide: function()
26669             {
26670                 _this.fireEvent('OverlayViewHide', _this);
26671             }
26672             
26673         });
26674     },
26675     
26676     fromLatLngToContainerPixel: function(event)
26677     {
26678         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26679     },
26680     
26681     isApplied: function() 
26682     {
26683         return this.getGmapContext() == false ? false : true;
26684     },
26685     
26686     getGmapContext: function() 
26687     {
26688         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26689     },
26690     
26691     GMapContext: function() 
26692     {
26693         var position = new google.maps.LatLng(this.latitude, this.longitude);
26694         
26695         var _map = new google.maps.Map(this.el.dom, {
26696             center: position,
26697             zoom: this.zoom,
26698             mapTypeId: this.mapTypeId,
26699             mapTypeControl: this.mapTypeControl,
26700             disableDoubleClickZoom: this.disableDoubleClickZoom,
26701             scrollwheel: this.scrollwheel,
26702             streetViewControl: this.streetViewControl,
26703             locationName: this.locationName,
26704             draggable: this.draggable,
26705             enableAutocomplete: this.enableAutocomplete,
26706             enableReverseGeocode: this.enableReverseGeocode
26707         });
26708         
26709         var _marker = new google.maps.Marker({
26710             position: position,
26711             map: _map,
26712             title: this.markerTitle,
26713             draggable: this.draggable
26714         });
26715         
26716         return {
26717             map: _map,
26718             marker: _marker,
26719             circle: null,
26720             location: position,
26721             radius: this.radius,
26722             locationName: this.locationName,
26723             addressComponents: {
26724                 formatted_address: null,
26725                 addressLine1: null,
26726                 addressLine2: null,
26727                 streetName: null,
26728                 streetNumber: null,
26729                 city: null,
26730                 district: null,
26731                 state: null,
26732                 stateOrProvince: null
26733             },
26734             settings: this,
26735             domContainer: this.el.dom,
26736             geodecoder: new google.maps.Geocoder()
26737         };
26738     },
26739     
26740     drawCircle: function(center, radius, options) 
26741     {
26742         if (this.gMapContext.circle != null) {
26743             this.gMapContext.circle.setMap(null);
26744         }
26745         if (radius > 0) {
26746             radius *= 1;
26747             options = Roo.apply({}, options, {
26748                 strokeColor: "#0000FF",
26749                 strokeOpacity: .35,
26750                 strokeWeight: 2,
26751                 fillColor: "#0000FF",
26752                 fillOpacity: .2
26753             });
26754             
26755             options.map = this.gMapContext.map;
26756             options.radius = radius;
26757             options.center = center;
26758             this.gMapContext.circle = new google.maps.Circle(options);
26759             return this.gMapContext.circle;
26760         }
26761         
26762         return null;
26763     },
26764     
26765     setPosition: function(location) 
26766     {
26767         this.gMapContext.location = location;
26768         this.gMapContext.marker.setPosition(location);
26769         this.gMapContext.map.panTo(location);
26770         this.drawCircle(location, this.gMapContext.radius, {});
26771         
26772         var _this = this;
26773         
26774         if (this.gMapContext.settings.enableReverseGeocode) {
26775             this.gMapContext.geodecoder.geocode({
26776                 latLng: this.gMapContext.location
26777             }, function(results, status) {
26778                 
26779                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26780                     _this.gMapContext.locationName = results[0].formatted_address;
26781                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26782                     
26783                     _this.fireEvent('positionchanged', this, location);
26784                 }
26785             });
26786             
26787             return;
26788         }
26789         
26790         this.fireEvent('positionchanged', this, location);
26791     },
26792     
26793     resize: function()
26794     {
26795         google.maps.event.trigger(this.gMapContext.map, "resize");
26796         
26797         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26798         
26799         this.fireEvent('resize', this);
26800     },
26801     
26802     setPositionByLatLng: function(latitude, longitude)
26803     {
26804         this.setPosition(new google.maps.LatLng(latitude, longitude));
26805     },
26806     
26807     getCurrentPosition: function() 
26808     {
26809         return {
26810             latitude: this.gMapContext.location.lat(),
26811             longitude: this.gMapContext.location.lng()
26812         };
26813     },
26814     
26815     getAddressName: function() 
26816     {
26817         return this.gMapContext.locationName;
26818     },
26819     
26820     getAddressComponents: function() 
26821     {
26822         return this.gMapContext.addressComponents;
26823     },
26824     
26825     address_component_from_google_geocode: function(address_components) 
26826     {
26827         var result = {};
26828         
26829         for (var i = 0; i < address_components.length; i++) {
26830             var component = address_components[i];
26831             if (component.types.indexOf("postal_code") >= 0) {
26832                 result.postalCode = component.short_name;
26833             } else if (component.types.indexOf("street_number") >= 0) {
26834                 result.streetNumber = component.short_name;
26835             } else if (component.types.indexOf("route") >= 0) {
26836                 result.streetName = component.short_name;
26837             } else if (component.types.indexOf("neighborhood") >= 0) {
26838                 result.city = component.short_name;
26839             } else if (component.types.indexOf("locality") >= 0) {
26840                 result.city = component.short_name;
26841             } else if (component.types.indexOf("sublocality") >= 0) {
26842                 result.district = component.short_name;
26843             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26844                 result.stateOrProvince = component.short_name;
26845             } else if (component.types.indexOf("country") >= 0) {
26846                 result.country = component.short_name;
26847             }
26848         }
26849         
26850         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26851         result.addressLine2 = "";
26852         return result;
26853     },
26854     
26855     setZoomLevel: function(zoom)
26856     {
26857         this.gMapContext.map.setZoom(zoom);
26858     },
26859     
26860     show: function()
26861     {
26862         if(!this.el){
26863             return;
26864         }
26865         
26866         this.el.show();
26867         
26868         this.resize();
26869         
26870         this.fireEvent('show', this);
26871     },
26872     
26873     hide: function()
26874     {
26875         if(!this.el){
26876             return;
26877         }
26878         
26879         this.el.hide();
26880         
26881         this.fireEvent('hide', this);
26882     }
26883     
26884 });
26885
26886 Roo.apply(Roo.bootstrap.LocationPicker, {
26887     
26888     OverlayView : function(map, options)
26889     {
26890         options = options || {};
26891         
26892         this.setMap(map);
26893     }
26894     
26895     
26896 });/*
26897  * - LGPL
26898  *
26899  * Alert
26900  * 
26901  */
26902
26903 /**
26904  * @class Roo.bootstrap.Alert
26905  * @extends Roo.bootstrap.Component
26906  * Bootstrap Alert class
26907  * @cfg {String} title The title of alert
26908  * @cfg {String} html The content of alert
26909  * @cfg {String} weight (  success | info | warning | danger )
26910  * @cfg {String} faicon font-awesomeicon
26911  * 
26912  * @constructor
26913  * Create a new alert
26914  * @param {Object} config The config object
26915  */
26916
26917
26918 Roo.bootstrap.Alert = function(config){
26919     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26920     
26921 };
26922
26923 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26924     
26925     title: '',
26926     html: '',
26927     weight: false,
26928     faicon: false,
26929     
26930     getAutoCreate : function()
26931     {
26932         
26933         var cfg = {
26934             tag : 'div',
26935             cls : 'alert',
26936             cn : [
26937                 {
26938                     tag : 'i',
26939                     cls : 'roo-alert-icon'
26940                     
26941                 },
26942                 {
26943                     tag : 'b',
26944                     cls : 'roo-alert-title',
26945                     html : this.title
26946                 },
26947                 {
26948                     tag : 'span',
26949                     cls : 'roo-alert-text',
26950                     html : this.html
26951                 }
26952             ]
26953         };
26954         
26955         if(this.faicon){
26956             cfg.cn[0].cls += ' fa ' + this.faicon;
26957         }
26958         
26959         if(this.weight){
26960             cfg.cls += ' alert-' + this.weight;
26961         }
26962         
26963         return cfg;
26964     },
26965     
26966     initEvents: function() 
26967     {
26968         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26969     },
26970     
26971     setTitle : function(str)
26972     {
26973         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26974     },
26975     
26976     setText : function(str)
26977     {
26978         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26979     },
26980     
26981     setWeight : function(weight)
26982     {
26983         if(this.weight){
26984             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26985         }
26986         
26987         this.weight = weight;
26988         
26989         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26990     },
26991     
26992     setIcon : function(icon)
26993     {
26994         if(this.faicon){
26995             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26996         }
26997         
26998         this.faicon = icon;
26999         
27000         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27001     },
27002     
27003     hide: function() 
27004     {
27005         this.el.hide();   
27006     },
27007     
27008     show: function() 
27009     {  
27010         this.el.show();   
27011     }
27012     
27013 });
27014
27015  
27016 /*
27017 * Licence: LGPL
27018 */
27019
27020 /**
27021  * @class Roo.bootstrap.UploadCropbox
27022  * @extends Roo.bootstrap.Component
27023  * Bootstrap UploadCropbox class
27024  * @cfg {String} emptyText show when image has been loaded
27025  * @cfg {String} rotateNotify show when image too small to rotate
27026  * @cfg {Number} errorTimeout default 3000
27027  * @cfg {Number} minWidth default 300
27028  * @cfg {Number} minHeight default 300
27029  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27030  * @cfg {Boolean} isDocument (true|false) default false
27031  * @cfg {String} url action url
27032  * @cfg {String} paramName default 'imageUpload'
27033  * @cfg {String} method default POST
27034  * @cfg {Boolean} loadMask (true|false) default true
27035  * @cfg {Boolean} loadingText default 'Loading...'
27036  * 
27037  * @constructor
27038  * Create a new UploadCropbox
27039  * @param {Object} config The config object
27040  */
27041
27042 Roo.bootstrap.UploadCropbox = function(config){
27043     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27044     
27045     this.addEvents({
27046         /**
27047          * @event beforeselectfile
27048          * Fire before select file
27049          * @param {Roo.bootstrap.UploadCropbox} this
27050          */
27051         "beforeselectfile" : true,
27052         /**
27053          * @event initial
27054          * Fire after initEvent
27055          * @param {Roo.bootstrap.UploadCropbox} this
27056          */
27057         "initial" : true,
27058         /**
27059          * @event crop
27060          * Fire after initEvent
27061          * @param {Roo.bootstrap.UploadCropbox} this
27062          * @param {String} data
27063          */
27064         "crop" : true,
27065         /**
27066          * @event prepare
27067          * Fire when preparing the file data
27068          * @param {Roo.bootstrap.UploadCropbox} this
27069          * @param {Object} file
27070          */
27071         "prepare" : true,
27072         /**
27073          * @event exception
27074          * Fire when get exception
27075          * @param {Roo.bootstrap.UploadCropbox} this
27076          * @param {XMLHttpRequest} xhr
27077          */
27078         "exception" : true,
27079         /**
27080          * @event beforeloadcanvas
27081          * Fire before load the canvas
27082          * @param {Roo.bootstrap.UploadCropbox} this
27083          * @param {String} src
27084          */
27085         "beforeloadcanvas" : true,
27086         /**
27087          * @event trash
27088          * Fire when trash image
27089          * @param {Roo.bootstrap.UploadCropbox} this
27090          */
27091         "trash" : true,
27092         /**
27093          * @event download
27094          * Fire when download the image
27095          * @param {Roo.bootstrap.UploadCropbox} this
27096          */
27097         "download" : true,
27098         /**
27099          * @event footerbuttonclick
27100          * Fire when footerbuttonclick
27101          * @param {Roo.bootstrap.UploadCropbox} this
27102          * @param {String} type
27103          */
27104         "footerbuttonclick" : true,
27105         /**
27106          * @event resize
27107          * Fire when resize
27108          * @param {Roo.bootstrap.UploadCropbox} this
27109          */
27110         "resize" : true,
27111         /**
27112          * @event rotate
27113          * Fire when rotate the image
27114          * @param {Roo.bootstrap.UploadCropbox} this
27115          * @param {String} pos
27116          */
27117         "rotate" : true,
27118         /**
27119          * @event inspect
27120          * Fire when inspect the file
27121          * @param {Roo.bootstrap.UploadCropbox} this
27122          * @param {Object} file
27123          */
27124         "inspect" : true,
27125         /**
27126          * @event upload
27127          * Fire when xhr upload the file
27128          * @param {Roo.bootstrap.UploadCropbox} this
27129          * @param {Object} data
27130          */
27131         "upload" : true,
27132         /**
27133          * @event arrange
27134          * Fire when arrange the file data
27135          * @param {Roo.bootstrap.UploadCropbox} this
27136          * @param {Object} formData
27137          */
27138         "arrange" : true
27139     });
27140     
27141     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27142 };
27143
27144 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27145     
27146     emptyText : 'Click to upload image',
27147     rotateNotify : 'Image is too small to rotate',
27148     errorTimeout : 3000,
27149     scale : 0,
27150     baseScale : 1,
27151     rotate : 0,
27152     dragable : false,
27153     pinching : false,
27154     mouseX : 0,
27155     mouseY : 0,
27156     cropData : false,
27157     minWidth : 300,
27158     minHeight : 300,
27159     file : false,
27160     exif : {},
27161     baseRotate : 1,
27162     cropType : 'image/jpeg',
27163     buttons : false,
27164     canvasLoaded : false,
27165     isDocument : false,
27166     method : 'POST',
27167     paramName : 'imageUpload',
27168     loadMask : true,
27169     loadingText : 'Loading...',
27170     maskEl : false,
27171     
27172     getAutoCreate : function()
27173     {
27174         var cfg = {
27175             tag : 'div',
27176             cls : 'roo-upload-cropbox',
27177             cn : [
27178                 {
27179                     tag : 'input',
27180                     cls : 'roo-upload-cropbox-selector',
27181                     type : 'file'
27182                 },
27183                 {
27184                     tag : 'div',
27185                     cls : 'roo-upload-cropbox-body',
27186                     style : 'cursor:pointer',
27187                     cn : [
27188                         {
27189                             tag : 'div',
27190                             cls : 'roo-upload-cropbox-preview'
27191                         },
27192                         {
27193                             tag : 'div',
27194                             cls : 'roo-upload-cropbox-thumb'
27195                         },
27196                         {
27197                             tag : 'div',
27198                             cls : 'roo-upload-cropbox-empty-notify',
27199                             html : this.emptyText
27200                         },
27201                         {
27202                             tag : 'div',
27203                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27204                             html : this.rotateNotify
27205                         }
27206                     ]
27207                 },
27208                 {
27209                     tag : 'div',
27210                     cls : 'roo-upload-cropbox-footer',
27211                     cn : {
27212                         tag : 'div',
27213                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27214                         cn : []
27215                     }
27216                 }
27217             ]
27218         };
27219         
27220         return cfg;
27221     },
27222     
27223     onRender : function(ct, position)
27224     {
27225         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27226         
27227         if (this.buttons.length) {
27228             
27229             Roo.each(this.buttons, function(bb) {
27230                 
27231                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27232                 
27233                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27234                 
27235             }, this);
27236         }
27237         
27238         if(this.loadMask){
27239             this.maskEl = this.el;
27240         }
27241     },
27242     
27243     initEvents : function()
27244     {
27245         this.urlAPI = (window.createObjectURL && window) || 
27246                                 (window.URL && URL.revokeObjectURL && URL) || 
27247                                 (window.webkitURL && webkitURL);
27248                         
27249         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27250         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27251         
27252         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27253         this.selectorEl.hide();
27254         
27255         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27256         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27257         
27258         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27259         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27260         this.thumbEl.hide();
27261         
27262         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27263         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27264         
27265         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27266         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27267         this.errorEl.hide();
27268         
27269         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27270         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27271         this.footerEl.hide();
27272         
27273         this.setThumbBoxSize();
27274         
27275         this.bind();
27276         
27277         this.resize();
27278         
27279         this.fireEvent('initial', this);
27280     },
27281
27282     bind : function()
27283     {
27284         var _this = this;
27285         
27286         window.addEventListener("resize", function() { _this.resize(); } );
27287         
27288         this.bodyEl.on('click', this.beforeSelectFile, this);
27289         
27290         if(Roo.isTouch){
27291             this.bodyEl.on('touchstart', this.onTouchStart, this);
27292             this.bodyEl.on('touchmove', this.onTouchMove, this);
27293             this.bodyEl.on('touchend', this.onTouchEnd, this);
27294         }
27295         
27296         if(!Roo.isTouch){
27297             this.bodyEl.on('mousedown', this.onMouseDown, this);
27298             this.bodyEl.on('mousemove', this.onMouseMove, this);
27299             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27300             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27301             Roo.get(document).on('mouseup', this.onMouseUp, this);
27302         }
27303         
27304         this.selectorEl.on('change', this.onFileSelected, this);
27305     },
27306     
27307     reset : function()
27308     {    
27309         this.scale = 0;
27310         this.baseScale = 1;
27311         this.rotate = 0;
27312         this.baseRotate = 1;
27313         this.dragable = false;
27314         this.pinching = false;
27315         this.mouseX = 0;
27316         this.mouseY = 0;
27317         this.cropData = false;
27318         this.notifyEl.dom.innerHTML = this.emptyText;
27319         
27320         this.selectorEl.dom.value = '';
27321         
27322     },
27323     
27324     resize : function()
27325     {
27326         if(this.fireEvent('resize', this) != false){
27327             this.setThumbBoxPosition();
27328             this.setCanvasPosition();
27329         }
27330     },
27331     
27332     onFooterButtonClick : function(e, el, o, type)
27333     {
27334         switch (type) {
27335             case 'rotate-left' :
27336                 this.onRotateLeft(e);
27337                 break;
27338             case 'rotate-right' :
27339                 this.onRotateRight(e);
27340                 break;
27341             case 'picture' :
27342                 this.beforeSelectFile(e);
27343                 break;
27344             case 'trash' :
27345                 this.trash(e);
27346                 break;
27347             case 'crop' :
27348                 this.crop(e);
27349                 break;
27350             case 'download' :
27351                 this.download(e);
27352                 break;
27353             default :
27354                 break;
27355         }
27356         
27357         this.fireEvent('footerbuttonclick', this, type);
27358     },
27359     
27360     beforeSelectFile : function(e)
27361     {
27362         e.preventDefault();
27363         
27364         if(this.fireEvent('beforeselectfile', this) != false){
27365             this.selectorEl.dom.click();
27366         }
27367     },
27368     
27369     onFileSelected : function(e)
27370     {
27371         e.preventDefault();
27372         
27373         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27374             return;
27375         }
27376         
27377         var file = this.selectorEl.dom.files[0];
27378         
27379         if(this.fireEvent('inspect', this, file) != false){
27380             this.prepare(file);
27381         }
27382         
27383     },
27384     
27385     trash : function(e)
27386     {
27387         this.fireEvent('trash', this);
27388     },
27389     
27390     download : function(e)
27391     {
27392         this.fireEvent('download', this);
27393     },
27394     
27395     loadCanvas : function(src)
27396     {   
27397         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27398             
27399             this.reset();
27400             
27401             this.imageEl = document.createElement('img');
27402             
27403             var _this = this;
27404             
27405             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27406             
27407             this.imageEl.src = src;
27408         }
27409     },
27410     
27411     onLoadCanvas : function()
27412     {   
27413         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27414         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27415         
27416         this.bodyEl.un('click', this.beforeSelectFile, this);
27417         
27418         this.notifyEl.hide();
27419         this.thumbEl.show();
27420         this.footerEl.show();
27421         
27422         this.baseRotateLevel();
27423         
27424         if(this.isDocument){
27425             this.setThumbBoxSize();
27426         }
27427         
27428         this.setThumbBoxPosition();
27429         
27430         this.baseScaleLevel();
27431         
27432         this.draw();
27433         
27434         this.resize();
27435         
27436         this.canvasLoaded = true;
27437         
27438         if(this.loadMask){
27439             this.maskEl.unmask();
27440         }
27441         
27442     },
27443     
27444     setCanvasPosition : function()
27445     {   
27446         if(!this.canvasEl){
27447             return;
27448         }
27449         
27450         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27451         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27452         
27453         this.previewEl.setLeft(pw);
27454         this.previewEl.setTop(ph);
27455         
27456     },
27457     
27458     onMouseDown : function(e)
27459     {   
27460         e.stopEvent();
27461         
27462         this.dragable = true;
27463         this.pinching = false;
27464         
27465         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27466             this.dragable = false;
27467             return;
27468         }
27469         
27470         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27471         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27472         
27473     },
27474     
27475     onMouseMove : function(e)
27476     {   
27477         e.stopEvent();
27478         
27479         if(!this.canvasLoaded){
27480             return;
27481         }
27482         
27483         if (!this.dragable){
27484             return;
27485         }
27486         
27487         var minX = Math.ceil(this.thumbEl.getLeft(true));
27488         var minY = Math.ceil(this.thumbEl.getTop(true));
27489         
27490         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27491         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27492         
27493         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27494         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27495         
27496         x = x - this.mouseX;
27497         y = y - this.mouseY;
27498         
27499         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27500         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27501         
27502         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27503         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27504         
27505         this.previewEl.setLeft(bgX);
27506         this.previewEl.setTop(bgY);
27507         
27508         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27509         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27510     },
27511     
27512     onMouseUp : function(e)
27513     {   
27514         e.stopEvent();
27515         
27516         this.dragable = false;
27517     },
27518     
27519     onMouseWheel : function(e)
27520     {   
27521         e.stopEvent();
27522         
27523         this.startScale = this.scale;
27524         
27525         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27526         
27527         if(!this.zoomable()){
27528             this.scale = this.startScale;
27529             return;
27530         }
27531         
27532         this.draw();
27533         
27534         return;
27535     },
27536     
27537     zoomable : function()
27538     {
27539         var minScale = this.thumbEl.getWidth() / this.minWidth;
27540         
27541         if(this.minWidth < this.minHeight){
27542             minScale = this.thumbEl.getHeight() / this.minHeight;
27543         }
27544         
27545         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27546         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27547         
27548         if(
27549                 this.isDocument &&
27550                 (this.rotate == 0 || this.rotate == 180) && 
27551                 (
27552                     width > this.imageEl.OriginWidth || 
27553                     height > this.imageEl.OriginHeight ||
27554                     (width < this.minWidth && height < this.minHeight)
27555                 )
27556         ){
27557             return false;
27558         }
27559         
27560         if(
27561                 this.isDocument &&
27562                 (this.rotate == 90 || this.rotate == 270) && 
27563                 (
27564                     width > this.imageEl.OriginWidth || 
27565                     height > this.imageEl.OriginHeight ||
27566                     (width < this.minHeight && height < this.minWidth)
27567                 )
27568         ){
27569             return false;
27570         }
27571         
27572         if(
27573                 !this.isDocument &&
27574                 (this.rotate == 0 || this.rotate == 180) && 
27575                 (
27576                     width < this.minWidth || 
27577                     width > this.imageEl.OriginWidth || 
27578                     height < this.minHeight || 
27579                     height > this.imageEl.OriginHeight
27580                 )
27581         ){
27582             return false;
27583         }
27584         
27585         if(
27586                 !this.isDocument &&
27587                 (this.rotate == 90 || this.rotate == 270) && 
27588                 (
27589                     width < this.minHeight || 
27590                     width > this.imageEl.OriginWidth || 
27591                     height < this.minWidth || 
27592                     height > this.imageEl.OriginHeight
27593                 )
27594         ){
27595             return false;
27596         }
27597         
27598         return true;
27599         
27600     },
27601     
27602     onRotateLeft : function(e)
27603     {   
27604         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27605             
27606             var minScale = this.thumbEl.getWidth() / this.minWidth;
27607             
27608             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27609             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27610             
27611             this.startScale = this.scale;
27612             
27613             while (this.getScaleLevel() < minScale){
27614             
27615                 this.scale = this.scale + 1;
27616                 
27617                 if(!this.zoomable()){
27618                     break;
27619                 }
27620                 
27621                 if(
27622                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27623                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27624                 ){
27625                     continue;
27626                 }
27627                 
27628                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27629
27630                 this.draw();
27631                 
27632                 return;
27633             }
27634             
27635             this.scale = this.startScale;
27636             
27637             this.onRotateFail();
27638             
27639             return false;
27640         }
27641         
27642         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27643
27644         if(this.isDocument){
27645             this.setThumbBoxSize();
27646             this.setThumbBoxPosition();
27647             this.setCanvasPosition();
27648         }
27649         
27650         this.draw();
27651         
27652         this.fireEvent('rotate', this, 'left');
27653         
27654     },
27655     
27656     onRotateRight : function(e)
27657     {
27658         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27659             
27660             var minScale = this.thumbEl.getWidth() / this.minWidth;
27661         
27662             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27663             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27664             
27665             this.startScale = this.scale;
27666             
27667             while (this.getScaleLevel() < minScale){
27668             
27669                 this.scale = this.scale + 1;
27670                 
27671                 if(!this.zoomable()){
27672                     break;
27673                 }
27674                 
27675                 if(
27676                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27677                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27678                 ){
27679                     continue;
27680                 }
27681                 
27682                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27683
27684                 this.draw();
27685                 
27686                 return;
27687             }
27688             
27689             this.scale = this.startScale;
27690             
27691             this.onRotateFail();
27692             
27693             return false;
27694         }
27695         
27696         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27697
27698         if(this.isDocument){
27699             this.setThumbBoxSize();
27700             this.setThumbBoxPosition();
27701             this.setCanvasPosition();
27702         }
27703         
27704         this.draw();
27705         
27706         this.fireEvent('rotate', this, 'right');
27707     },
27708     
27709     onRotateFail : function()
27710     {
27711         this.errorEl.show(true);
27712         
27713         var _this = this;
27714         
27715         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27716     },
27717     
27718     draw : function()
27719     {
27720         this.previewEl.dom.innerHTML = '';
27721         
27722         var canvasEl = document.createElement("canvas");
27723         
27724         var contextEl = canvasEl.getContext("2d");
27725         
27726         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27727         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27728         var center = this.imageEl.OriginWidth / 2;
27729         
27730         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27731             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27732             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27733             center = this.imageEl.OriginHeight / 2;
27734         }
27735         
27736         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27737         
27738         contextEl.translate(center, center);
27739         contextEl.rotate(this.rotate * Math.PI / 180);
27740
27741         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27742         
27743         this.canvasEl = document.createElement("canvas");
27744         
27745         this.contextEl = this.canvasEl.getContext("2d");
27746         
27747         switch (this.rotate) {
27748             case 0 :
27749                 
27750                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27751                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27752                 
27753                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27754                 
27755                 break;
27756             case 90 : 
27757                 
27758                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27759                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27760                 
27761                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27762                     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);
27763                     break;
27764                 }
27765                 
27766                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27767                 
27768                 break;
27769             case 180 :
27770                 
27771                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27772                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27773                 
27774                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27775                     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);
27776                     break;
27777                 }
27778                 
27779                 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);
27780                 
27781                 break;
27782             case 270 :
27783                 
27784                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27785                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27786         
27787                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27788                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27789                     break;
27790                 }
27791                 
27792                 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);
27793                 
27794                 break;
27795             default : 
27796                 break;
27797         }
27798         
27799         this.previewEl.appendChild(this.canvasEl);
27800         
27801         this.setCanvasPosition();
27802     },
27803     
27804     crop : function()
27805     {
27806         if(!this.canvasLoaded){
27807             return;
27808         }
27809         
27810         var imageCanvas = document.createElement("canvas");
27811         
27812         var imageContext = imageCanvas.getContext("2d");
27813         
27814         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27815         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27816         
27817         var center = imageCanvas.width / 2;
27818         
27819         imageContext.translate(center, center);
27820         
27821         imageContext.rotate(this.rotate * Math.PI / 180);
27822         
27823         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27824         
27825         var canvas = document.createElement("canvas");
27826         
27827         var context = canvas.getContext("2d");
27828                 
27829         canvas.width = this.minWidth;
27830         canvas.height = this.minHeight;
27831
27832         switch (this.rotate) {
27833             case 0 :
27834                 
27835                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27836                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27837                 
27838                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27839                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27840                 
27841                 var targetWidth = this.minWidth - 2 * x;
27842                 var targetHeight = this.minHeight - 2 * y;
27843                 
27844                 var scale = 1;
27845                 
27846                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27847                     scale = targetWidth / width;
27848                 }
27849                 
27850                 if(x > 0 && y == 0){
27851                     scale = targetHeight / height;
27852                 }
27853                 
27854                 if(x > 0 && y > 0){
27855                     scale = targetWidth / width;
27856                     
27857                     if(width < height){
27858                         scale = targetHeight / height;
27859                     }
27860                 }
27861                 
27862                 context.scale(scale, scale);
27863                 
27864                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27865                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27866
27867                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27868                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27869
27870                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27871                 
27872                 break;
27873             case 90 : 
27874                 
27875                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27876                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27877                 
27878                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27879                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27880                 
27881                 var targetWidth = this.minWidth - 2 * x;
27882                 var targetHeight = this.minHeight - 2 * y;
27883                 
27884                 var scale = 1;
27885                 
27886                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27887                     scale = targetWidth / width;
27888                 }
27889                 
27890                 if(x > 0 && y == 0){
27891                     scale = targetHeight / height;
27892                 }
27893                 
27894                 if(x > 0 && y > 0){
27895                     scale = targetWidth / width;
27896                     
27897                     if(width < height){
27898                         scale = targetHeight / height;
27899                     }
27900                 }
27901                 
27902                 context.scale(scale, scale);
27903                 
27904                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27905                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27906
27907                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27908                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27909                 
27910                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27911                 
27912                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27913                 
27914                 break;
27915             case 180 :
27916                 
27917                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27918                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27919                 
27920                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27921                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27922                 
27923                 var targetWidth = this.minWidth - 2 * x;
27924                 var targetHeight = this.minHeight - 2 * y;
27925                 
27926                 var scale = 1;
27927                 
27928                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27929                     scale = targetWidth / width;
27930                 }
27931                 
27932                 if(x > 0 && y == 0){
27933                     scale = targetHeight / height;
27934                 }
27935                 
27936                 if(x > 0 && y > 0){
27937                     scale = targetWidth / width;
27938                     
27939                     if(width < height){
27940                         scale = targetHeight / height;
27941                     }
27942                 }
27943                 
27944                 context.scale(scale, scale);
27945                 
27946                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27947                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27948
27949                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27950                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27951
27952                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27953                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27954                 
27955                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27956                 
27957                 break;
27958             case 270 :
27959                 
27960                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27961                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27962                 
27963                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27964                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27965                 
27966                 var targetWidth = this.minWidth - 2 * x;
27967                 var targetHeight = this.minHeight - 2 * y;
27968                 
27969                 var scale = 1;
27970                 
27971                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27972                     scale = targetWidth / width;
27973                 }
27974                 
27975                 if(x > 0 && y == 0){
27976                     scale = targetHeight / height;
27977                 }
27978                 
27979                 if(x > 0 && y > 0){
27980                     scale = targetWidth / width;
27981                     
27982                     if(width < height){
27983                         scale = targetHeight / height;
27984                     }
27985                 }
27986                 
27987                 context.scale(scale, scale);
27988                 
27989                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27990                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27991
27992                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27993                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27994                 
27995                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27996                 
27997                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27998                 
27999                 break;
28000             default : 
28001                 break;
28002         }
28003         
28004         this.cropData = canvas.toDataURL(this.cropType);
28005         
28006         if(this.fireEvent('crop', this, this.cropData) !== false){
28007             this.process(this.file, this.cropData);
28008         }
28009         
28010         return;
28011         
28012     },
28013     
28014     setThumbBoxSize : function()
28015     {
28016         var width, height;
28017         
28018         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28019             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28020             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28021             
28022             this.minWidth = width;
28023             this.minHeight = height;
28024             
28025             if(this.rotate == 90 || this.rotate == 270){
28026                 this.minWidth = height;
28027                 this.minHeight = width;
28028             }
28029         }
28030         
28031         height = 300;
28032         width = Math.ceil(this.minWidth * height / this.minHeight);
28033         
28034         if(this.minWidth > this.minHeight){
28035             width = 300;
28036             height = Math.ceil(this.minHeight * width / this.minWidth);
28037         }
28038         
28039         this.thumbEl.setStyle({
28040             width : width + 'px',
28041             height : height + 'px'
28042         });
28043
28044         return;
28045             
28046     },
28047     
28048     setThumbBoxPosition : function()
28049     {
28050         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28051         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28052         
28053         this.thumbEl.setLeft(x);
28054         this.thumbEl.setTop(y);
28055         
28056     },
28057     
28058     baseRotateLevel : function()
28059     {
28060         this.baseRotate = 1;
28061         
28062         if(
28063                 typeof(this.exif) != 'undefined' &&
28064                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28065                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28066         ){
28067             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28068         }
28069         
28070         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28071         
28072     },
28073     
28074     baseScaleLevel : function()
28075     {
28076         var width, height;
28077         
28078         if(this.isDocument){
28079             
28080             if(this.baseRotate == 6 || this.baseRotate == 8){
28081             
28082                 height = this.thumbEl.getHeight();
28083                 this.baseScale = height / this.imageEl.OriginWidth;
28084
28085                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28086                     width = this.thumbEl.getWidth();
28087                     this.baseScale = width / this.imageEl.OriginHeight;
28088                 }
28089
28090                 return;
28091             }
28092
28093             height = this.thumbEl.getHeight();
28094             this.baseScale = height / this.imageEl.OriginHeight;
28095
28096             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28097                 width = this.thumbEl.getWidth();
28098                 this.baseScale = width / this.imageEl.OriginWidth;
28099             }
28100
28101             return;
28102         }
28103         
28104         if(this.baseRotate == 6 || this.baseRotate == 8){
28105             
28106             width = this.thumbEl.getHeight();
28107             this.baseScale = width / this.imageEl.OriginHeight;
28108             
28109             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28110                 height = this.thumbEl.getWidth();
28111                 this.baseScale = height / this.imageEl.OriginHeight;
28112             }
28113             
28114             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28115                 height = this.thumbEl.getWidth();
28116                 this.baseScale = height / this.imageEl.OriginHeight;
28117                 
28118                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28119                     width = this.thumbEl.getHeight();
28120                     this.baseScale = width / this.imageEl.OriginWidth;
28121                 }
28122             }
28123             
28124             return;
28125         }
28126         
28127         width = this.thumbEl.getWidth();
28128         this.baseScale = width / this.imageEl.OriginWidth;
28129         
28130         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28131             height = this.thumbEl.getHeight();
28132             this.baseScale = height / this.imageEl.OriginHeight;
28133         }
28134         
28135         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28136             
28137             height = this.thumbEl.getHeight();
28138             this.baseScale = height / this.imageEl.OriginHeight;
28139             
28140             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28141                 width = this.thumbEl.getWidth();
28142                 this.baseScale = width / this.imageEl.OriginWidth;
28143             }
28144             
28145         }
28146         
28147         return;
28148     },
28149     
28150     getScaleLevel : function()
28151     {
28152         return this.baseScale * Math.pow(1.1, this.scale);
28153     },
28154     
28155     onTouchStart : function(e)
28156     {
28157         if(!this.canvasLoaded){
28158             this.beforeSelectFile(e);
28159             return;
28160         }
28161         
28162         var touches = e.browserEvent.touches;
28163         
28164         if(!touches){
28165             return;
28166         }
28167         
28168         if(touches.length == 1){
28169             this.onMouseDown(e);
28170             return;
28171         }
28172         
28173         if(touches.length != 2){
28174             return;
28175         }
28176         
28177         var coords = [];
28178         
28179         for(var i = 0, finger; finger = touches[i]; i++){
28180             coords.push(finger.pageX, finger.pageY);
28181         }
28182         
28183         var x = Math.pow(coords[0] - coords[2], 2);
28184         var y = Math.pow(coords[1] - coords[3], 2);
28185         
28186         this.startDistance = Math.sqrt(x + y);
28187         
28188         this.startScale = this.scale;
28189         
28190         this.pinching = true;
28191         this.dragable = false;
28192         
28193     },
28194     
28195     onTouchMove : function(e)
28196     {
28197         if(!this.pinching && !this.dragable){
28198             return;
28199         }
28200         
28201         var touches = e.browserEvent.touches;
28202         
28203         if(!touches){
28204             return;
28205         }
28206         
28207         if(this.dragable){
28208             this.onMouseMove(e);
28209             return;
28210         }
28211         
28212         var coords = [];
28213         
28214         for(var i = 0, finger; finger = touches[i]; i++){
28215             coords.push(finger.pageX, finger.pageY);
28216         }
28217         
28218         var x = Math.pow(coords[0] - coords[2], 2);
28219         var y = Math.pow(coords[1] - coords[3], 2);
28220         
28221         this.endDistance = Math.sqrt(x + y);
28222         
28223         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28224         
28225         if(!this.zoomable()){
28226             this.scale = this.startScale;
28227             return;
28228         }
28229         
28230         this.draw();
28231         
28232     },
28233     
28234     onTouchEnd : function(e)
28235     {
28236         this.pinching = false;
28237         this.dragable = false;
28238         
28239     },
28240     
28241     process : function(file, crop)
28242     {
28243         if(this.loadMask){
28244             this.maskEl.mask(this.loadingText);
28245         }
28246         
28247         this.xhr = new XMLHttpRequest();
28248         
28249         file.xhr = this.xhr;
28250
28251         this.xhr.open(this.method, this.url, true);
28252         
28253         var headers = {
28254             "Accept": "application/json",
28255             "Cache-Control": "no-cache",
28256             "X-Requested-With": "XMLHttpRequest"
28257         };
28258         
28259         for (var headerName in headers) {
28260             var headerValue = headers[headerName];
28261             if (headerValue) {
28262                 this.xhr.setRequestHeader(headerName, headerValue);
28263             }
28264         }
28265         
28266         var _this = this;
28267         
28268         this.xhr.onload = function()
28269         {
28270             _this.xhrOnLoad(_this.xhr);
28271         }
28272         
28273         this.xhr.onerror = function()
28274         {
28275             _this.xhrOnError(_this.xhr);
28276         }
28277         
28278         var formData = new FormData();
28279
28280         formData.append('returnHTML', 'NO');
28281         
28282         if(crop){
28283             formData.append('crop', crop);
28284         }
28285         
28286         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28287             formData.append(this.paramName, file, file.name);
28288         }
28289         
28290         if(typeof(file.filename) != 'undefined'){
28291             formData.append('filename', file.filename);
28292         }
28293         
28294         if(typeof(file.mimetype) != 'undefined'){
28295             formData.append('mimetype', file.mimetype);
28296         }
28297         
28298         if(this.fireEvent('arrange', this, formData) != false){
28299             this.xhr.send(formData);
28300         };
28301     },
28302     
28303     xhrOnLoad : function(xhr)
28304     {
28305         if(this.loadMask){
28306             this.maskEl.unmask();
28307         }
28308         
28309         if (xhr.readyState !== 4) {
28310             this.fireEvent('exception', this, xhr);
28311             return;
28312         }
28313
28314         var response = Roo.decode(xhr.responseText);
28315         
28316         if(!response.success){
28317             this.fireEvent('exception', this, xhr);
28318             return;
28319         }
28320         
28321         var response = Roo.decode(xhr.responseText);
28322         
28323         this.fireEvent('upload', this, response);
28324         
28325     },
28326     
28327     xhrOnError : function()
28328     {
28329         if(this.loadMask){
28330             this.maskEl.unmask();
28331         }
28332         
28333         Roo.log('xhr on error');
28334         
28335         var response = Roo.decode(xhr.responseText);
28336           
28337         Roo.log(response);
28338         
28339     },
28340     
28341     prepare : function(file)
28342     {   
28343         if(this.loadMask){
28344             this.maskEl.mask(this.loadingText);
28345         }
28346         
28347         this.file = false;
28348         this.exif = {};
28349         
28350         if(typeof(file) === 'string'){
28351             this.loadCanvas(file);
28352             return;
28353         }
28354         
28355         if(!file || !this.urlAPI){
28356             return;
28357         }
28358         
28359         this.file = file;
28360         this.cropType = file.type;
28361         
28362         var _this = this;
28363         
28364         if(this.fireEvent('prepare', this, this.file) != false){
28365             
28366             var reader = new FileReader();
28367             
28368             reader.onload = function (e) {
28369                 if (e.target.error) {
28370                     Roo.log(e.target.error);
28371                     return;
28372                 }
28373                 
28374                 var buffer = e.target.result,
28375                     dataView = new DataView(buffer),
28376                     offset = 2,
28377                     maxOffset = dataView.byteLength - 4,
28378                     markerBytes,
28379                     markerLength;
28380                 
28381                 if (dataView.getUint16(0) === 0xffd8) {
28382                     while (offset < maxOffset) {
28383                         markerBytes = dataView.getUint16(offset);
28384                         
28385                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28386                             markerLength = dataView.getUint16(offset + 2) + 2;
28387                             if (offset + markerLength > dataView.byteLength) {
28388                                 Roo.log('Invalid meta data: Invalid segment size.');
28389                                 break;
28390                             }
28391                             
28392                             if(markerBytes == 0xffe1){
28393                                 _this.parseExifData(
28394                                     dataView,
28395                                     offset,
28396                                     markerLength
28397                                 );
28398                             }
28399                             
28400                             offset += markerLength;
28401                             
28402                             continue;
28403                         }
28404                         
28405                         break;
28406                     }
28407                     
28408                 }
28409                 
28410                 var url = _this.urlAPI.createObjectURL(_this.file);
28411                 
28412                 _this.loadCanvas(url);
28413                 
28414                 return;
28415             }
28416             
28417             reader.readAsArrayBuffer(this.file);
28418             
28419         }
28420         
28421     },
28422     
28423     parseExifData : function(dataView, offset, length)
28424     {
28425         var tiffOffset = offset + 10,
28426             littleEndian,
28427             dirOffset;
28428     
28429         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28430             // No Exif data, might be XMP data instead
28431             return;
28432         }
28433         
28434         // Check for the ASCII code for "Exif" (0x45786966):
28435         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28436             // No Exif data, might be XMP data instead
28437             return;
28438         }
28439         if (tiffOffset + 8 > dataView.byteLength) {
28440             Roo.log('Invalid Exif data: Invalid segment size.');
28441             return;
28442         }
28443         // Check for the two null bytes:
28444         if (dataView.getUint16(offset + 8) !== 0x0000) {
28445             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28446             return;
28447         }
28448         // Check the byte alignment:
28449         switch (dataView.getUint16(tiffOffset)) {
28450         case 0x4949:
28451             littleEndian = true;
28452             break;
28453         case 0x4D4D:
28454             littleEndian = false;
28455             break;
28456         default:
28457             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28458             return;
28459         }
28460         // Check for the TIFF tag marker (0x002A):
28461         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28462             Roo.log('Invalid Exif data: Missing TIFF marker.');
28463             return;
28464         }
28465         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28466         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28467         
28468         this.parseExifTags(
28469             dataView,
28470             tiffOffset,
28471             tiffOffset + dirOffset,
28472             littleEndian
28473         );
28474     },
28475     
28476     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28477     {
28478         var tagsNumber,
28479             dirEndOffset,
28480             i;
28481         if (dirOffset + 6 > dataView.byteLength) {
28482             Roo.log('Invalid Exif data: Invalid directory offset.');
28483             return;
28484         }
28485         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28486         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28487         if (dirEndOffset + 4 > dataView.byteLength) {
28488             Roo.log('Invalid Exif data: Invalid directory size.');
28489             return;
28490         }
28491         for (i = 0; i < tagsNumber; i += 1) {
28492             this.parseExifTag(
28493                 dataView,
28494                 tiffOffset,
28495                 dirOffset + 2 + 12 * i, // tag offset
28496                 littleEndian
28497             );
28498         }
28499         // Return the offset to the next directory:
28500         return dataView.getUint32(dirEndOffset, littleEndian);
28501     },
28502     
28503     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28504     {
28505         var tag = dataView.getUint16(offset, littleEndian);
28506         
28507         this.exif[tag] = this.getExifValue(
28508             dataView,
28509             tiffOffset,
28510             offset,
28511             dataView.getUint16(offset + 2, littleEndian), // tag type
28512             dataView.getUint32(offset + 4, littleEndian), // tag length
28513             littleEndian
28514         );
28515     },
28516     
28517     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28518     {
28519         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28520             tagSize,
28521             dataOffset,
28522             values,
28523             i,
28524             str,
28525             c;
28526     
28527         if (!tagType) {
28528             Roo.log('Invalid Exif data: Invalid tag type.');
28529             return;
28530         }
28531         
28532         tagSize = tagType.size * length;
28533         // Determine if the value is contained in the dataOffset bytes,
28534         // or if the value at the dataOffset is a pointer to the actual data:
28535         dataOffset = tagSize > 4 ?
28536                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28537         if (dataOffset + tagSize > dataView.byteLength) {
28538             Roo.log('Invalid Exif data: Invalid data offset.');
28539             return;
28540         }
28541         if (length === 1) {
28542             return tagType.getValue(dataView, dataOffset, littleEndian);
28543         }
28544         values = [];
28545         for (i = 0; i < length; i += 1) {
28546             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28547         }
28548         
28549         if (tagType.ascii) {
28550             str = '';
28551             // Concatenate the chars:
28552             for (i = 0; i < values.length; i += 1) {
28553                 c = values[i];
28554                 // Ignore the terminating NULL byte(s):
28555                 if (c === '\u0000') {
28556                     break;
28557                 }
28558                 str += c;
28559             }
28560             return str;
28561         }
28562         return values;
28563     }
28564     
28565 });
28566
28567 Roo.apply(Roo.bootstrap.UploadCropbox, {
28568     tags : {
28569         'Orientation': 0x0112
28570     },
28571     
28572     Orientation: {
28573             1: 0, //'top-left',
28574 //            2: 'top-right',
28575             3: 180, //'bottom-right',
28576 //            4: 'bottom-left',
28577 //            5: 'left-top',
28578             6: 90, //'right-top',
28579 //            7: 'right-bottom',
28580             8: 270 //'left-bottom'
28581     },
28582     
28583     exifTagTypes : {
28584         // byte, 8-bit unsigned int:
28585         1: {
28586             getValue: function (dataView, dataOffset) {
28587                 return dataView.getUint8(dataOffset);
28588             },
28589             size: 1
28590         },
28591         // ascii, 8-bit byte:
28592         2: {
28593             getValue: function (dataView, dataOffset) {
28594                 return String.fromCharCode(dataView.getUint8(dataOffset));
28595             },
28596             size: 1,
28597             ascii: true
28598         },
28599         // short, 16 bit int:
28600         3: {
28601             getValue: function (dataView, dataOffset, littleEndian) {
28602                 return dataView.getUint16(dataOffset, littleEndian);
28603             },
28604             size: 2
28605         },
28606         // long, 32 bit int:
28607         4: {
28608             getValue: function (dataView, dataOffset, littleEndian) {
28609                 return dataView.getUint32(dataOffset, littleEndian);
28610             },
28611             size: 4
28612         },
28613         // rational = two long values, first is numerator, second is denominator:
28614         5: {
28615             getValue: function (dataView, dataOffset, littleEndian) {
28616                 return dataView.getUint32(dataOffset, littleEndian) /
28617                     dataView.getUint32(dataOffset + 4, littleEndian);
28618             },
28619             size: 8
28620         },
28621         // slong, 32 bit signed int:
28622         9: {
28623             getValue: function (dataView, dataOffset, littleEndian) {
28624                 return dataView.getInt32(dataOffset, littleEndian);
28625             },
28626             size: 4
28627         },
28628         // srational, two slongs, first is numerator, second is denominator:
28629         10: {
28630             getValue: function (dataView, dataOffset, littleEndian) {
28631                 return dataView.getInt32(dataOffset, littleEndian) /
28632                     dataView.getInt32(dataOffset + 4, littleEndian);
28633             },
28634             size: 8
28635         }
28636     },
28637     
28638     footer : {
28639         STANDARD : [
28640             {
28641                 tag : 'div',
28642                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28643                 action : 'rotate-left',
28644                 cn : [
28645                     {
28646                         tag : 'button',
28647                         cls : 'btn btn-default',
28648                         html : '<i class="fa fa-undo"></i>'
28649                     }
28650                 ]
28651             },
28652             {
28653                 tag : 'div',
28654                 cls : 'btn-group roo-upload-cropbox-picture',
28655                 action : 'picture',
28656                 cn : [
28657                     {
28658                         tag : 'button',
28659                         cls : 'btn btn-default',
28660                         html : '<i class="fa fa-picture-o"></i>'
28661                     }
28662                 ]
28663             },
28664             {
28665                 tag : 'div',
28666                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28667                 action : 'rotate-right',
28668                 cn : [
28669                     {
28670                         tag : 'button',
28671                         cls : 'btn btn-default',
28672                         html : '<i class="fa fa-repeat"></i>'
28673                     }
28674                 ]
28675             }
28676         ],
28677         DOCUMENT : [
28678             {
28679                 tag : 'div',
28680                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28681                 action : 'rotate-left',
28682                 cn : [
28683                     {
28684                         tag : 'button',
28685                         cls : 'btn btn-default',
28686                         html : '<i class="fa fa-undo"></i>'
28687                     }
28688                 ]
28689             },
28690             {
28691                 tag : 'div',
28692                 cls : 'btn-group roo-upload-cropbox-download',
28693                 action : 'download',
28694                 cn : [
28695                     {
28696                         tag : 'button',
28697                         cls : 'btn btn-default',
28698                         html : '<i class="fa fa-download"></i>'
28699                     }
28700                 ]
28701             },
28702             {
28703                 tag : 'div',
28704                 cls : 'btn-group roo-upload-cropbox-crop',
28705                 action : 'crop',
28706                 cn : [
28707                     {
28708                         tag : 'button',
28709                         cls : 'btn btn-default',
28710                         html : '<i class="fa fa-crop"></i>'
28711                     }
28712                 ]
28713             },
28714             {
28715                 tag : 'div',
28716                 cls : 'btn-group roo-upload-cropbox-trash',
28717                 action : 'trash',
28718                 cn : [
28719                     {
28720                         tag : 'button',
28721                         cls : 'btn btn-default',
28722                         html : '<i class="fa fa-trash"></i>'
28723                     }
28724                 ]
28725             },
28726             {
28727                 tag : 'div',
28728                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28729                 action : 'rotate-right',
28730                 cn : [
28731                     {
28732                         tag : 'button',
28733                         cls : 'btn btn-default',
28734                         html : '<i class="fa fa-repeat"></i>'
28735                     }
28736                 ]
28737             }
28738         ],
28739         ROTATOR : [
28740             {
28741                 tag : 'div',
28742                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28743                 action : 'rotate-left',
28744                 cn : [
28745                     {
28746                         tag : 'button',
28747                         cls : 'btn btn-default',
28748                         html : '<i class="fa fa-undo"></i>'
28749                     }
28750                 ]
28751             },
28752             {
28753                 tag : 'div',
28754                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28755                 action : 'rotate-right',
28756                 cn : [
28757                     {
28758                         tag : 'button',
28759                         cls : 'btn btn-default',
28760                         html : '<i class="fa fa-repeat"></i>'
28761                     }
28762                 ]
28763             }
28764         ]
28765     }
28766 });
28767
28768 /*
28769 * Licence: LGPL
28770 */
28771
28772 /**
28773  * @class Roo.bootstrap.DocumentManager
28774  * @extends Roo.bootstrap.Component
28775  * Bootstrap DocumentManager class
28776  * @cfg {String} paramName default 'imageUpload'
28777  * @cfg {String} toolTipName default 'filename'
28778  * @cfg {String} method default POST
28779  * @cfg {String} url action url
28780  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28781  * @cfg {Boolean} multiple multiple upload default true
28782  * @cfg {Number} thumbSize default 300
28783  * @cfg {String} fieldLabel
28784  * @cfg {Number} labelWidth default 4
28785  * @cfg {String} labelAlign (left|top) default left
28786  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28787 * @cfg {Number} labellg set the width of label (1-12)
28788  * @cfg {Number} labelmd set the width of label (1-12)
28789  * @cfg {Number} labelsm set the width of label (1-12)
28790  * @cfg {Number} labelxs set the width of label (1-12)
28791  * 
28792  * @constructor
28793  * Create a new DocumentManager
28794  * @param {Object} config The config object
28795  */
28796
28797 Roo.bootstrap.DocumentManager = function(config){
28798     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28799     
28800     this.files = [];
28801     this.delegates = [];
28802     
28803     this.addEvents({
28804         /**
28805          * @event initial
28806          * Fire when initial the DocumentManager
28807          * @param {Roo.bootstrap.DocumentManager} this
28808          */
28809         "initial" : true,
28810         /**
28811          * @event inspect
28812          * inspect selected file
28813          * @param {Roo.bootstrap.DocumentManager} this
28814          * @param {File} file
28815          */
28816         "inspect" : true,
28817         /**
28818          * @event exception
28819          * Fire when xhr load exception
28820          * @param {Roo.bootstrap.DocumentManager} this
28821          * @param {XMLHttpRequest} xhr
28822          */
28823         "exception" : true,
28824         /**
28825          * @event afterupload
28826          * Fire when xhr load exception
28827          * @param {Roo.bootstrap.DocumentManager} this
28828          * @param {XMLHttpRequest} xhr
28829          */
28830         "afterupload" : true,
28831         /**
28832          * @event prepare
28833          * prepare the form data
28834          * @param {Roo.bootstrap.DocumentManager} this
28835          * @param {Object} formData
28836          */
28837         "prepare" : true,
28838         /**
28839          * @event remove
28840          * Fire when remove the file
28841          * @param {Roo.bootstrap.DocumentManager} this
28842          * @param {Object} file
28843          */
28844         "remove" : true,
28845         /**
28846          * @event refresh
28847          * Fire after refresh the file
28848          * @param {Roo.bootstrap.DocumentManager} this
28849          */
28850         "refresh" : true,
28851         /**
28852          * @event click
28853          * Fire after click the image
28854          * @param {Roo.bootstrap.DocumentManager} this
28855          * @param {Object} file
28856          */
28857         "click" : true,
28858         /**
28859          * @event edit
28860          * Fire when upload a image and editable set to true
28861          * @param {Roo.bootstrap.DocumentManager} this
28862          * @param {Object} file
28863          */
28864         "edit" : true,
28865         /**
28866          * @event beforeselectfile
28867          * Fire before select file
28868          * @param {Roo.bootstrap.DocumentManager} this
28869          */
28870         "beforeselectfile" : true,
28871         /**
28872          * @event process
28873          * Fire before process file
28874          * @param {Roo.bootstrap.DocumentManager} this
28875          * @param {Object} file
28876          */
28877         "process" : true,
28878         /**
28879          * @event previewrendered
28880          * Fire when preview rendered
28881          * @param {Roo.bootstrap.DocumentManager} this
28882          * @param {Object} file
28883          */
28884         "previewrendered" : true,
28885         /**
28886          */
28887         "previewResize" : true
28888         
28889     });
28890 };
28891
28892 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28893     
28894     boxes : 0,
28895     inputName : '',
28896     thumbSize : 300,
28897     multiple : true,
28898     files : false,
28899     method : 'POST',
28900     url : '',
28901     paramName : 'imageUpload',
28902     toolTipName : 'filename',
28903     fieldLabel : '',
28904     labelWidth : 4,
28905     labelAlign : 'left',
28906     editable : true,
28907     delegates : false,
28908     xhr : false, 
28909     
28910     labellg : 0,
28911     labelmd : 0,
28912     labelsm : 0,
28913     labelxs : 0,
28914     
28915     getAutoCreate : function()
28916     {   
28917         var managerWidget = {
28918             tag : 'div',
28919             cls : 'roo-document-manager',
28920             cn : [
28921                 {
28922                     tag : 'input',
28923                     cls : 'roo-document-manager-selector',
28924                     type : 'file'
28925                 },
28926                 {
28927                     tag : 'div',
28928                     cls : 'roo-document-manager-uploader',
28929                     cn : [
28930                         {
28931                             tag : 'div',
28932                             cls : 'roo-document-manager-upload-btn',
28933                             html : '<i class="fa fa-plus"></i>'
28934                         }
28935                     ]
28936                     
28937                 }
28938             ]
28939         };
28940         
28941         var content = [
28942             {
28943                 tag : 'div',
28944                 cls : 'column col-md-12',
28945                 cn : managerWidget
28946             }
28947         ];
28948         
28949         if(this.fieldLabel.length){
28950             
28951             content = [
28952                 {
28953                     tag : 'div',
28954                     cls : 'column col-md-12',
28955                     html : this.fieldLabel
28956                 },
28957                 {
28958                     tag : 'div',
28959                     cls : 'column col-md-12',
28960                     cn : managerWidget
28961                 }
28962             ];
28963
28964             if(this.labelAlign == 'left'){
28965                 content = [
28966                     {
28967                         tag : 'div',
28968                         cls : 'column',
28969                         html : this.fieldLabel
28970                     },
28971                     {
28972                         tag : 'div',
28973                         cls : 'column',
28974                         cn : managerWidget
28975                     }
28976                 ];
28977                 
28978                 if(this.labelWidth > 12){
28979                     content[0].style = "width: " + this.labelWidth + 'px';
28980                 }
28981
28982                 if(this.labelWidth < 13 && this.labelmd == 0){
28983                     this.labelmd = this.labelWidth;
28984                 }
28985
28986                 if(this.labellg > 0){
28987                     content[0].cls += ' col-lg-' + this.labellg;
28988                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28989                 }
28990
28991                 if(this.labelmd > 0){
28992                     content[0].cls += ' col-md-' + this.labelmd;
28993                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28994                 }
28995
28996                 if(this.labelsm > 0){
28997                     content[0].cls += ' col-sm-' + this.labelsm;
28998                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28999                 }
29000
29001                 if(this.labelxs > 0){
29002                     content[0].cls += ' col-xs-' + this.labelxs;
29003                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29004                 }
29005                 
29006             }
29007         }
29008         
29009         var cfg = {
29010             tag : 'div',
29011             cls : 'row clearfix',
29012             cn : content
29013         };
29014         
29015         return cfg;
29016         
29017     },
29018     
29019     initEvents : function()
29020     {
29021         this.managerEl = this.el.select('.roo-document-manager', true).first();
29022         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29023         
29024         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29025         this.selectorEl.hide();
29026         
29027         if(this.multiple){
29028             this.selectorEl.attr('multiple', 'multiple');
29029         }
29030         
29031         this.selectorEl.on('change', this.onFileSelected, this);
29032         
29033         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29034         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29035         
29036         this.uploader.on('click', this.onUploaderClick, this);
29037         
29038         this.renderProgressDialog();
29039         
29040         var _this = this;
29041         
29042         window.addEventListener("resize", function() { _this.refresh(); } );
29043         
29044         this.fireEvent('initial', this);
29045     },
29046     
29047     renderProgressDialog : function()
29048     {
29049         var _this = this;
29050         
29051         this.progressDialog = new Roo.bootstrap.Modal({
29052             cls : 'roo-document-manager-progress-dialog',
29053             allow_close : false,
29054             title : '',
29055             buttons : [
29056                 {
29057                     name  :'cancel',
29058                     weight : 'danger',
29059                     html : 'Cancel'
29060                 }
29061             ], 
29062             listeners : { 
29063                 btnclick : function() {
29064                     _this.uploadCancel();
29065                     this.hide();
29066                 }
29067             }
29068         });
29069          
29070         this.progressDialog.render(Roo.get(document.body));
29071          
29072         this.progress = new Roo.bootstrap.Progress({
29073             cls : 'roo-document-manager-progress',
29074             active : true,
29075             striped : true
29076         });
29077         
29078         this.progress.render(this.progressDialog.getChildContainer());
29079         
29080         this.progressBar = new Roo.bootstrap.ProgressBar({
29081             cls : 'roo-document-manager-progress-bar',
29082             aria_valuenow : 0,
29083             aria_valuemin : 0,
29084             aria_valuemax : 12,
29085             panel : 'success'
29086         });
29087         
29088         this.progressBar.render(this.progress.getChildContainer());
29089     },
29090     
29091     onUploaderClick : function(e)
29092     {
29093         e.preventDefault();
29094      
29095         if(this.fireEvent('beforeselectfile', this) != false){
29096             this.selectorEl.dom.click();
29097         }
29098         
29099     },
29100     
29101     onFileSelected : function(e)
29102     {
29103         e.preventDefault();
29104         
29105         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29106             return;
29107         }
29108         
29109         Roo.each(this.selectorEl.dom.files, function(file){
29110             if(this.fireEvent('inspect', this, file) != false){
29111                 this.files.push(file);
29112             }
29113         }, this);
29114         
29115         this.queue();
29116         
29117     },
29118     
29119     queue : function()
29120     {
29121         this.selectorEl.dom.value = '';
29122         
29123         if(!this.files || !this.files.length){
29124             return;
29125         }
29126         
29127         if(this.boxes > 0 && this.files.length > this.boxes){
29128             this.files = this.files.slice(0, this.boxes);
29129         }
29130         
29131         this.uploader.show();
29132         
29133         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29134             this.uploader.hide();
29135         }
29136         
29137         var _this = this;
29138         
29139         var files = [];
29140         
29141         var docs = [];
29142         
29143         Roo.each(this.files, function(file){
29144             
29145             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29146                 var f = this.renderPreview(file);
29147                 files.push(f);
29148                 return;
29149             }
29150             
29151             if(file.type.indexOf('image') != -1){
29152                 this.delegates.push(
29153                     (function(){
29154                         _this.process(file);
29155                     }).createDelegate(this)
29156                 );
29157         
29158                 return;
29159             }
29160             
29161             docs.push(
29162                 (function(){
29163                     _this.process(file);
29164                 }).createDelegate(this)
29165             );
29166             
29167         }, this);
29168         
29169         this.files = files;
29170         
29171         this.delegates = this.delegates.concat(docs);
29172         
29173         if(!this.delegates.length){
29174             this.refresh();
29175             return;
29176         }
29177         
29178         this.progressBar.aria_valuemax = this.delegates.length;
29179         
29180         this.arrange();
29181         
29182         return;
29183     },
29184     
29185     arrange : function()
29186     {
29187         if(!this.delegates.length){
29188             this.progressDialog.hide();
29189             this.refresh();
29190             return;
29191         }
29192         
29193         var delegate = this.delegates.shift();
29194         
29195         this.progressDialog.show();
29196         
29197         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29198         
29199         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29200         
29201         delegate();
29202     },
29203     
29204     refresh : function()
29205     {
29206         this.uploader.show();
29207         
29208         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29209             this.uploader.hide();
29210         }
29211         
29212         Roo.isTouch ? this.closable(false) : this.closable(true);
29213         
29214         this.fireEvent('refresh', this);
29215     },
29216     
29217     onRemove : function(e, el, o)
29218     {
29219         e.preventDefault();
29220         
29221         this.fireEvent('remove', this, o);
29222         
29223     },
29224     
29225     remove : function(o)
29226     {
29227         var files = [];
29228         
29229         Roo.each(this.files, function(file){
29230             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29231                 files.push(file);
29232                 return;
29233             }
29234
29235             o.target.remove();
29236
29237         }, this);
29238         
29239         this.files = files;
29240         
29241         this.refresh();
29242     },
29243     
29244     clear : function()
29245     {
29246         Roo.each(this.files, function(file){
29247             if(!file.target){
29248                 return;
29249             }
29250             
29251             file.target.remove();
29252
29253         }, this);
29254         
29255         this.files = [];
29256         
29257         this.refresh();
29258     },
29259     
29260     onClick : function(e, el, o)
29261     {
29262         e.preventDefault();
29263         
29264         this.fireEvent('click', this, o);
29265         
29266     },
29267     
29268     closable : function(closable)
29269     {
29270         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29271             
29272             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29273             
29274             if(closable){
29275                 el.show();
29276                 return;
29277             }
29278             
29279             el.hide();
29280             
29281         }, this);
29282     },
29283     
29284     xhrOnLoad : function(xhr)
29285     {
29286         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29287             el.remove();
29288         }, this);
29289         
29290         if (xhr.readyState !== 4) {
29291             this.arrange();
29292             this.fireEvent('exception', this, xhr);
29293             return;
29294         }
29295
29296         var response = Roo.decode(xhr.responseText);
29297         
29298         if(!response.success){
29299             this.arrange();
29300             this.fireEvent('exception', this, xhr);
29301             return;
29302         }
29303         
29304         var file = this.renderPreview(response.data);
29305         
29306         this.files.push(file);
29307         
29308         this.arrange();
29309         
29310         this.fireEvent('afterupload', this, xhr);
29311         
29312     },
29313     
29314     xhrOnError : function(xhr)
29315     {
29316         Roo.log('xhr on error');
29317         
29318         var response = Roo.decode(xhr.responseText);
29319           
29320         Roo.log(response);
29321         
29322         this.arrange();
29323     },
29324     
29325     process : function(file)
29326     {
29327         if(this.fireEvent('process', this, file) !== false){
29328             if(this.editable && file.type.indexOf('image') != -1){
29329                 this.fireEvent('edit', this, file);
29330                 return;
29331             }
29332
29333             this.uploadStart(file, false);
29334
29335             return;
29336         }
29337         
29338     },
29339     
29340     uploadStart : function(file, crop)
29341     {
29342         this.xhr = new XMLHttpRequest();
29343         
29344         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29345             this.arrange();
29346             return;
29347         }
29348         
29349         file.xhr = this.xhr;
29350             
29351         this.managerEl.createChild({
29352             tag : 'div',
29353             cls : 'roo-document-manager-loading',
29354             cn : [
29355                 {
29356                     tag : 'div',
29357                     tooltip : file.name,
29358                     cls : 'roo-document-manager-thumb',
29359                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29360                 }
29361             ]
29362
29363         });
29364
29365         this.xhr.open(this.method, this.url, true);
29366         
29367         var headers = {
29368             "Accept": "application/json",
29369             "Cache-Control": "no-cache",
29370             "X-Requested-With": "XMLHttpRequest"
29371         };
29372         
29373         for (var headerName in headers) {
29374             var headerValue = headers[headerName];
29375             if (headerValue) {
29376                 this.xhr.setRequestHeader(headerName, headerValue);
29377             }
29378         }
29379         
29380         var _this = this;
29381         
29382         this.xhr.onload = function()
29383         {
29384             _this.xhrOnLoad(_this.xhr);
29385         }
29386         
29387         this.xhr.onerror = function()
29388         {
29389             _this.xhrOnError(_this.xhr);
29390         }
29391         
29392         var formData = new FormData();
29393
29394         formData.append('returnHTML', 'NO');
29395         
29396         if(crop){
29397             formData.append('crop', crop);
29398         }
29399         
29400         formData.append(this.paramName, file, file.name);
29401         
29402         var options = {
29403             file : file, 
29404             manually : false
29405         };
29406         
29407         if(this.fireEvent('prepare', this, formData, options) != false){
29408             
29409             if(options.manually){
29410                 return;
29411             }
29412             
29413             this.xhr.send(formData);
29414             return;
29415         };
29416         
29417         this.uploadCancel();
29418     },
29419     
29420     uploadCancel : function()
29421     {
29422         if (this.xhr) {
29423             this.xhr.abort();
29424         }
29425         
29426         this.delegates = [];
29427         
29428         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29429             el.remove();
29430         }, this);
29431         
29432         this.arrange();
29433     },
29434     
29435     renderPreview : function(file)
29436     {
29437         if(typeof(file.target) != 'undefined' && file.target){
29438             return file;
29439         }
29440         
29441         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29442         
29443         var previewEl = this.managerEl.createChild({
29444             tag : 'div',
29445             cls : 'roo-document-manager-preview',
29446             cn : [
29447                 {
29448                     tag : 'div',
29449                     tooltip : file[this.toolTipName],
29450                     cls : 'roo-document-manager-thumb',
29451                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29452                 },
29453                 {
29454                     tag : 'button',
29455                     cls : 'close',
29456                     html : '<i class="fa fa-times-circle"></i>'
29457                 }
29458             ]
29459         });
29460
29461         var close = previewEl.select('button.close', true).first();
29462
29463         close.on('click', this.onRemove, this, file);
29464
29465         file.target = previewEl;
29466
29467         var image = previewEl.select('img', true).first();
29468         
29469         var _this = this;
29470         
29471         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29472         
29473         image.on('click', this.onClick, this, file);
29474         
29475         this.fireEvent('previewrendered', this, file);
29476         
29477         return file;
29478         
29479     },
29480     
29481     onPreviewLoad : function(file, image)
29482     {
29483         if(typeof(file.target) == 'undefined' || !file.target){
29484             return;
29485         }
29486         
29487         var width = image.dom.naturalWidth || image.dom.width;
29488         var height = image.dom.naturalHeight || image.dom.height;
29489         
29490         if(!this.previewResize) {
29491             return;
29492         }
29493         
29494         if(width > height){
29495             file.target.addClass('wide');
29496             return;
29497         }
29498         
29499         file.target.addClass('tall');
29500         return;
29501         
29502     },
29503     
29504     uploadFromSource : function(file, crop)
29505     {
29506         this.xhr = new XMLHttpRequest();
29507         
29508         this.managerEl.createChild({
29509             tag : 'div',
29510             cls : 'roo-document-manager-loading',
29511             cn : [
29512                 {
29513                     tag : 'div',
29514                     tooltip : file.name,
29515                     cls : 'roo-document-manager-thumb',
29516                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29517                 }
29518             ]
29519
29520         });
29521
29522         this.xhr.open(this.method, this.url, true);
29523         
29524         var headers = {
29525             "Accept": "application/json",
29526             "Cache-Control": "no-cache",
29527             "X-Requested-With": "XMLHttpRequest"
29528         };
29529         
29530         for (var headerName in headers) {
29531             var headerValue = headers[headerName];
29532             if (headerValue) {
29533                 this.xhr.setRequestHeader(headerName, headerValue);
29534             }
29535         }
29536         
29537         var _this = this;
29538         
29539         this.xhr.onload = function()
29540         {
29541             _this.xhrOnLoad(_this.xhr);
29542         }
29543         
29544         this.xhr.onerror = function()
29545         {
29546             _this.xhrOnError(_this.xhr);
29547         }
29548         
29549         var formData = new FormData();
29550
29551         formData.append('returnHTML', 'NO');
29552         
29553         formData.append('crop', crop);
29554         
29555         if(typeof(file.filename) != 'undefined'){
29556             formData.append('filename', file.filename);
29557         }
29558         
29559         if(typeof(file.mimetype) != 'undefined'){
29560             formData.append('mimetype', file.mimetype);
29561         }
29562         
29563         Roo.log(formData);
29564         
29565         if(this.fireEvent('prepare', this, formData) != false){
29566             this.xhr.send(formData);
29567         };
29568     }
29569 });
29570
29571 /*
29572 * Licence: LGPL
29573 */
29574
29575 /**
29576  * @class Roo.bootstrap.DocumentViewer
29577  * @extends Roo.bootstrap.Component
29578  * Bootstrap DocumentViewer class
29579  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29580  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29581  * 
29582  * @constructor
29583  * Create a new DocumentViewer
29584  * @param {Object} config The config object
29585  */
29586
29587 Roo.bootstrap.DocumentViewer = function(config){
29588     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29589     
29590     this.addEvents({
29591         /**
29592          * @event initial
29593          * Fire after initEvent
29594          * @param {Roo.bootstrap.DocumentViewer} this
29595          */
29596         "initial" : true,
29597         /**
29598          * @event click
29599          * Fire after click
29600          * @param {Roo.bootstrap.DocumentViewer} this
29601          */
29602         "click" : true,
29603         /**
29604          * @event download
29605          * Fire after download button
29606          * @param {Roo.bootstrap.DocumentViewer} this
29607          */
29608         "download" : true,
29609         /**
29610          * @event trash
29611          * Fire after trash button
29612          * @param {Roo.bootstrap.DocumentViewer} this
29613          */
29614         "trash" : true
29615         
29616     });
29617 };
29618
29619 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29620     
29621     showDownload : true,
29622     
29623     showTrash : true,
29624     
29625     getAutoCreate : function()
29626     {
29627         var cfg = {
29628             tag : 'div',
29629             cls : 'roo-document-viewer',
29630             cn : [
29631                 {
29632                     tag : 'div',
29633                     cls : 'roo-document-viewer-body',
29634                     cn : [
29635                         {
29636                             tag : 'div',
29637                             cls : 'roo-document-viewer-thumb',
29638                             cn : [
29639                                 {
29640                                     tag : 'img',
29641                                     cls : 'roo-document-viewer-image'
29642                                 }
29643                             ]
29644                         }
29645                     ]
29646                 },
29647                 {
29648                     tag : 'div',
29649                     cls : 'roo-document-viewer-footer',
29650                     cn : {
29651                         tag : 'div',
29652                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29653                         cn : [
29654                             {
29655                                 tag : 'div',
29656                                 cls : 'btn-group roo-document-viewer-download',
29657                                 cn : [
29658                                     {
29659                                         tag : 'button',
29660                                         cls : 'btn btn-default',
29661                                         html : '<i class="fa fa-download"></i>'
29662                                     }
29663                                 ]
29664                             },
29665                             {
29666                                 tag : 'div',
29667                                 cls : 'btn-group roo-document-viewer-trash',
29668                                 cn : [
29669                                     {
29670                                         tag : 'button',
29671                                         cls : 'btn btn-default',
29672                                         html : '<i class="fa fa-trash"></i>'
29673                                     }
29674                                 ]
29675                             }
29676                         ]
29677                     }
29678                 }
29679             ]
29680         };
29681         
29682         return cfg;
29683     },
29684     
29685     initEvents : function()
29686     {
29687         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29688         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29689         
29690         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29691         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29692         
29693         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29694         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29695         
29696         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29697         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29698         
29699         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29700         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29701         
29702         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29703         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29704         
29705         this.bodyEl.on('click', this.onClick, this);
29706         this.downloadBtn.on('click', this.onDownload, this);
29707         this.trashBtn.on('click', this.onTrash, this);
29708         
29709         this.downloadBtn.hide();
29710         this.trashBtn.hide();
29711         
29712         if(this.showDownload){
29713             this.downloadBtn.show();
29714         }
29715         
29716         if(this.showTrash){
29717             this.trashBtn.show();
29718         }
29719         
29720         if(!this.showDownload && !this.showTrash) {
29721             this.footerEl.hide();
29722         }
29723         
29724     },
29725     
29726     initial : function()
29727     {
29728         this.fireEvent('initial', this);
29729         
29730     },
29731     
29732     onClick : function(e)
29733     {
29734         e.preventDefault();
29735         
29736         this.fireEvent('click', this);
29737     },
29738     
29739     onDownload : function(e)
29740     {
29741         e.preventDefault();
29742         
29743         this.fireEvent('download', this);
29744     },
29745     
29746     onTrash : function(e)
29747     {
29748         e.preventDefault();
29749         
29750         this.fireEvent('trash', this);
29751     }
29752     
29753 });
29754 /*
29755  * - LGPL
29756  *
29757  * nav progress bar
29758  * 
29759  */
29760
29761 /**
29762  * @class Roo.bootstrap.NavProgressBar
29763  * @extends Roo.bootstrap.Component
29764  * Bootstrap NavProgressBar class
29765  * 
29766  * @constructor
29767  * Create a new nav progress bar
29768  * @param {Object} config The config object
29769  */
29770
29771 Roo.bootstrap.NavProgressBar = function(config){
29772     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29773
29774     this.bullets = this.bullets || [];
29775    
29776 //    Roo.bootstrap.NavProgressBar.register(this);
29777      this.addEvents({
29778         /**
29779              * @event changed
29780              * Fires when the active item changes
29781              * @param {Roo.bootstrap.NavProgressBar} this
29782              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29783              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29784          */
29785         'changed': true
29786      });
29787     
29788 };
29789
29790 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29791     
29792     bullets : [],
29793     barItems : [],
29794     
29795     getAutoCreate : function()
29796     {
29797         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29798         
29799         cfg = {
29800             tag : 'div',
29801             cls : 'roo-navigation-bar-group',
29802             cn : [
29803                 {
29804                     tag : 'div',
29805                     cls : 'roo-navigation-top-bar'
29806                 },
29807                 {
29808                     tag : 'div',
29809                     cls : 'roo-navigation-bullets-bar',
29810                     cn : [
29811                         {
29812                             tag : 'ul',
29813                             cls : 'roo-navigation-bar'
29814                         }
29815                     ]
29816                 },
29817                 
29818                 {
29819                     tag : 'div',
29820                     cls : 'roo-navigation-bottom-bar'
29821                 }
29822             ]
29823             
29824         };
29825         
29826         return cfg;
29827         
29828     },
29829     
29830     initEvents: function() 
29831     {
29832         
29833     },
29834     
29835     onRender : function(ct, position) 
29836     {
29837         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29838         
29839         if(this.bullets.length){
29840             Roo.each(this.bullets, function(b){
29841                this.addItem(b);
29842             }, this);
29843         }
29844         
29845         this.format();
29846         
29847     },
29848     
29849     addItem : function(cfg)
29850     {
29851         var item = new Roo.bootstrap.NavProgressItem(cfg);
29852         
29853         item.parentId = this.id;
29854         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29855         
29856         if(cfg.html){
29857             var top = new Roo.bootstrap.Element({
29858                 tag : 'div',
29859                 cls : 'roo-navigation-bar-text'
29860             });
29861             
29862             var bottom = new Roo.bootstrap.Element({
29863                 tag : 'div',
29864                 cls : 'roo-navigation-bar-text'
29865             });
29866             
29867             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29868             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29869             
29870             var topText = new Roo.bootstrap.Element({
29871                 tag : 'span',
29872                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29873             });
29874             
29875             var bottomText = new Roo.bootstrap.Element({
29876                 tag : 'span',
29877                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29878             });
29879             
29880             topText.onRender(top.el, null);
29881             bottomText.onRender(bottom.el, null);
29882             
29883             item.topEl = top;
29884             item.bottomEl = bottom;
29885         }
29886         
29887         this.barItems.push(item);
29888         
29889         return item;
29890     },
29891     
29892     getActive : function()
29893     {
29894         var active = false;
29895         
29896         Roo.each(this.barItems, function(v){
29897             
29898             if (!v.isActive()) {
29899                 return;
29900             }
29901             
29902             active = v;
29903             return false;
29904             
29905         });
29906         
29907         return active;
29908     },
29909     
29910     setActiveItem : function(item)
29911     {
29912         var prev = false;
29913         
29914         Roo.each(this.barItems, function(v){
29915             if (v.rid == item.rid) {
29916                 return ;
29917             }
29918             
29919             if (v.isActive()) {
29920                 v.setActive(false);
29921                 prev = v;
29922             }
29923         });
29924
29925         item.setActive(true);
29926         
29927         this.fireEvent('changed', this, item, prev);
29928     },
29929     
29930     getBarItem: function(rid)
29931     {
29932         var ret = false;
29933         
29934         Roo.each(this.barItems, function(e) {
29935             if (e.rid != rid) {
29936                 return;
29937             }
29938             
29939             ret =  e;
29940             return false;
29941         });
29942         
29943         return ret;
29944     },
29945     
29946     indexOfItem : function(item)
29947     {
29948         var index = false;
29949         
29950         Roo.each(this.barItems, function(v, i){
29951             
29952             if (v.rid != item.rid) {
29953                 return;
29954             }
29955             
29956             index = i;
29957             return false
29958         });
29959         
29960         return index;
29961     },
29962     
29963     setActiveNext : function()
29964     {
29965         var i = this.indexOfItem(this.getActive());
29966         
29967         if (i > this.barItems.length) {
29968             return;
29969         }
29970         
29971         this.setActiveItem(this.barItems[i+1]);
29972     },
29973     
29974     setActivePrev : function()
29975     {
29976         var i = this.indexOfItem(this.getActive());
29977         
29978         if (i  < 1) {
29979             return;
29980         }
29981         
29982         this.setActiveItem(this.barItems[i-1]);
29983     },
29984     
29985     format : function()
29986     {
29987         if(!this.barItems.length){
29988             return;
29989         }
29990      
29991         var width = 100 / this.barItems.length;
29992         
29993         Roo.each(this.barItems, function(i){
29994             i.el.setStyle('width', width + '%');
29995             i.topEl.el.setStyle('width', width + '%');
29996             i.bottomEl.el.setStyle('width', width + '%');
29997         }, this);
29998         
29999     }
30000     
30001 });
30002 /*
30003  * - LGPL
30004  *
30005  * Nav Progress Item
30006  * 
30007  */
30008
30009 /**
30010  * @class Roo.bootstrap.NavProgressItem
30011  * @extends Roo.bootstrap.Component
30012  * Bootstrap NavProgressItem class
30013  * @cfg {String} rid the reference id
30014  * @cfg {Boolean} active (true|false) Is item active default false
30015  * @cfg {Boolean} disabled (true|false) Is item active default false
30016  * @cfg {String} html
30017  * @cfg {String} position (top|bottom) text position default bottom
30018  * @cfg {String} icon show icon instead of number
30019  * 
30020  * @constructor
30021  * Create a new NavProgressItem
30022  * @param {Object} config The config object
30023  */
30024 Roo.bootstrap.NavProgressItem = function(config){
30025     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30026     this.addEvents({
30027         // raw events
30028         /**
30029          * @event click
30030          * The raw click event for the entire grid.
30031          * @param {Roo.bootstrap.NavProgressItem} this
30032          * @param {Roo.EventObject} e
30033          */
30034         "click" : true
30035     });
30036    
30037 };
30038
30039 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30040     
30041     rid : '',
30042     active : false,
30043     disabled : false,
30044     html : '',
30045     position : 'bottom',
30046     icon : false,
30047     
30048     getAutoCreate : function()
30049     {
30050         var iconCls = 'roo-navigation-bar-item-icon';
30051         
30052         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30053         
30054         var cfg = {
30055             tag: 'li',
30056             cls: 'roo-navigation-bar-item',
30057             cn : [
30058                 {
30059                     tag : 'i',
30060                     cls : iconCls
30061                 }
30062             ]
30063         };
30064         
30065         if(this.active){
30066             cfg.cls += ' active';
30067         }
30068         if(this.disabled){
30069             cfg.cls += ' disabled';
30070         }
30071         
30072         return cfg;
30073     },
30074     
30075     disable : function()
30076     {
30077         this.setDisabled(true);
30078     },
30079     
30080     enable : function()
30081     {
30082         this.setDisabled(false);
30083     },
30084     
30085     initEvents: function() 
30086     {
30087         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30088         
30089         this.iconEl.on('click', this.onClick, this);
30090     },
30091     
30092     onClick : function(e)
30093     {
30094         e.preventDefault();
30095         
30096         if(this.disabled){
30097             return;
30098         }
30099         
30100         if(this.fireEvent('click', this, e) === false){
30101             return;
30102         };
30103         
30104         this.parent().setActiveItem(this);
30105     },
30106     
30107     isActive: function () 
30108     {
30109         return this.active;
30110     },
30111     
30112     setActive : function(state)
30113     {
30114         if(this.active == state){
30115             return;
30116         }
30117         
30118         this.active = state;
30119         
30120         if (state) {
30121             this.el.addClass('active');
30122             return;
30123         }
30124         
30125         this.el.removeClass('active');
30126         
30127         return;
30128     },
30129     
30130     setDisabled : function(state)
30131     {
30132         if(this.disabled == state){
30133             return;
30134         }
30135         
30136         this.disabled = state;
30137         
30138         if (state) {
30139             this.el.addClass('disabled');
30140             return;
30141         }
30142         
30143         this.el.removeClass('disabled');
30144     },
30145     
30146     tooltipEl : function()
30147     {
30148         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30149     }
30150 });
30151  
30152
30153  /*
30154  * - LGPL
30155  *
30156  * FieldLabel
30157  * 
30158  */
30159
30160 /**
30161  * @class Roo.bootstrap.FieldLabel
30162  * @extends Roo.bootstrap.Component
30163  * Bootstrap FieldLabel class
30164  * @cfg {String} html contents of the element
30165  * @cfg {String} tag tag of the element default label
30166  * @cfg {String} cls class of the element
30167  * @cfg {String} target label target 
30168  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30169  * @cfg {String} invalidClass default "text-warning"
30170  * @cfg {String} validClass default "text-success"
30171  * @cfg {String} iconTooltip default "This field is required"
30172  * @cfg {String} indicatorpos (left|right) default left
30173  * 
30174  * @constructor
30175  * Create a new FieldLabel
30176  * @param {Object} config The config object
30177  */
30178
30179 Roo.bootstrap.FieldLabel = function(config){
30180     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30181     
30182     this.addEvents({
30183             /**
30184              * @event invalid
30185              * Fires after the field has been marked as invalid.
30186              * @param {Roo.form.FieldLabel} this
30187              * @param {String} msg The validation message
30188              */
30189             invalid : true,
30190             /**
30191              * @event valid
30192              * Fires after the field has been validated with no errors.
30193              * @param {Roo.form.FieldLabel} this
30194              */
30195             valid : true
30196         });
30197 };
30198
30199 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30200     
30201     tag: 'label',
30202     cls: '',
30203     html: '',
30204     target: '',
30205     allowBlank : true,
30206     invalidClass : 'has-warning',
30207     validClass : 'has-success',
30208     iconTooltip : 'This field is required',
30209     indicatorpos : 'left',
30210     
30211     getAutoCreate : function(){
30212         
30213         var cls = "";
30214         if (!this.allowBlank) {
30215             cls  = "visible";
30216         }
30217         
30218         var cfg = {
30219             tag : this.tag,
30220             cls : 'roo-bootstrap-field-label ' + this.cls,
30221             for : this.target,
30222             cn : [
30223                 {
30224                     tag : 'i',
30225                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30226                     tooltip : this.iconTooltip
30227                 },
30228                 {
30229                     tag : 'span',
30230                     html : this.html
30231                 }
30232             ] 
30233         };
30234         
30235         if(this.indicatorpos == 'right'){
30236             var cfg = {
30237                 tag : this.tag,
30238                 cls : 'roo-bootstrap-field-label ' + this.cls,
30239                 for : this.target,
30240                 cn : [
30241                     {
30242                         tag : 'span',
30243                         html : this.html
30244                     },
30245                     {
30246                         tag : 'i',
30247                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30248                         tooltip : this.iconTooltip
30249                     }
30250                 ] 
30251             };
30252         }
30253         
30254         return cfg;
30255     },
30256     
30257     initEvents: function() 
30258     {
30259         Roo.bootstrap.Element.superclass.initEvents.call(this);
30260         
30261         this.indicator = this.indicatorEl();
30262         
30263         if(this.indicator){
30264             this.indicator.removeClass('visible');
30265             this.indicator.addClass('invisible');
30266         }
30267         
30268         Roo.bootstrap.FieldLabel.register(this);
30269     },
30270     
30271     indicatorEl : function()
30272     {
30273         var indicator = this.el.select('i.roo-required-indicator',true).first();
30274         
30275         if(!indicator){
30276             return false;
30277         }
30278         
30279         return indicator;
30280         
30281     },
30282     
30283     /**
30284      * Mark this field as valid
30285      */
30286     markValid : function()
30287     {
30288         if(this.indicator){
30289             this.indicator.removeClass('visible');
30290             this.indicator.addClass('invisible');
30291         }
30292         
30293         this.el.removeClass(this.invalidClass);
30294         
30295         this.el.addClass(this.validClass);
30296         
30297         this.fireEvent('valid', this);
30298     },
30299     
30300     /**
30301      * Mark this field as invalid
30302      * @param {String} msg The validation message
30303      */
30304     markInvalid : function(msg)
30305     {
30306         if(this.indicator){
30307             this.indicator.removeClass('invisible');
30308             this.indicator.addClass('visible');
30309         }
30310         
30311         this.el.removeClass(this.validClass);
30312         
30313         this.el.addClass(this.invalidClass);
30314         
30315         this.fireEvent('invalid', this, msg);
30316     }
30317     
30318    
30319 });
30320
30321 Roo.apply(Roo.bootstrap.FieldLabel, {
30322     
30323     groups: {},
30324     
30325      /**
30326     * register a FieldLabel Group
30327     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30328     */
30329     register : function(label)
30330     {
30331         if(this.groups.hasOwnProperty(label.target)){
30332             return;
30333         }
30334      
30335         this.groups[label.target] = label;
30336         
30337     },
30338     /**
30339     * fetch a FieldLabel Group based on the target
30340     * @param {string} target
30341     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30342     */
30343     get: function(target) {
30344         if (typeof(this.groups[target]) == 'undefined') {
30345             return false;
30346         }
30347         
30348         return this.groups[target] ;
30349     }
30350 });
30351
30352  
30353
30354  /*
30355  * - LGPL
30356  *
30357  * page DateSplitField.
30358  * 
30359  */
30360
30361
30362 /**
30363  * @class Roo.bootstrap.DateSplitField
30364  * @extends Roo.bootstrap.Component
30365  * Bootstrap DateSplitField class
30366  * @cfg {string} fieldLabel - the label associated
30367  * @cfg {Number} labelWidth set the width of label (0-12)
30368  * @cfg {String} labelAlign (top|left)
30369  * @cfg {Boolean} dayAllowBlank (true|false) default false
30370  * @cfg {Boolean} monthAllowBlank (true|false) default false
30371  * @cfg {Boolean} yearAllowBlank (true|false) default false
30372  * @cfg {string} dayPlaceholder 
30373  * @cfg {string} monthPlaceholder
30374  * @cfg {string} yearPlaceholder
30375  * @cfg {string} dayFormat default 'd'
30376  * @cfg {string} monthFormat default 'm'
30377  * @cfg {string} yearFormat default 'Y'
30378  * @cfg {Number} labellg set the width of label (1-12)
30379  * @cfg {Number} labelmd set the width of label (1-12)
30380  * @cfg {Number} labelsm set the width of label (1-12)
30381  * @cfg {Number} labelxs set the width of label (1-12)
30382
30383  *     
30384  * @constructor
30385  * Create a new DateSplitField
30386  * @param {Object} config The config object
30387  */
30388
30389 Roo.bootstrap.DateSplitField = function(config){
30390     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30391     
30392     this.addEvents({
30393         // raw events
30394          /**
30395          * @event years
30396          * getting the data of years
30397          * @param {Roo.bootstrap.DateSplitField} this
30398          * @param {Object} years
30399          */
30400         "years" : true,
30401         /**
30402          * @event days
30403          * getting the data of days
30404          * @param {Roo.bootstrap.DateSplitField} this
30405          * @param {Object} days
30406          */
30407         "days" : true,
30408         /**
30409          * @event invalid
30410          * Fires after the field has been marked as invalid.
30411          * @param {Roo.form.Field} this
30412          * @param {String} msg The validation message
30413          */
30414         invalid : true,
30415        /**
30416          * @event valid
30417          * Fires after the field has been validated with no errors.
30418          * @param {Roo.form.Field} this
30419          */
30420         valid : true
30421     });
30422 };
30423
30424 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30425     
30426     fieldLabel : '',
30427     labelAlign : 'top',
30428     labelWidth : 3,
30429     dayAllowBlank : false,
30430     monthAllowBlank : false,
30431     yearAllowBlank : false,
30432     dayPlaceholder : '',
30433     monthPlaceholder : '',
30434     yearPlaceholder : '',
30435     dayFormat : 'd',
30436     monthFormat : 'm',
30437     yearFormat : 'Y',
30438     isFormField : true,
30439     labellg : 0,
30440     labelmd : 0,
30441     labelsm : 0,
30442     labelxs : 0,
30443     
30444     getAutoCreate : function()
30445     {
30446         var cfg = {
30447             tag : 'div',
30448             cls : 'row roo-date-split-field-group',
30449             cn : [
30450                 {
30451                     tag : 'input',
30452                     type : 'hidden',
30453                     cls : 'form-hidden-field roo-date-split-field-group-value',
30454                     name : this.name
30455                 }
30456             ]
30457         };
30458         
30459         var labelCls = 'col-md-12';
30460         var contentCls = 'col-md-4';
30461         
30462         if(this.fieldLabel){
30463             
30464             var label = {
30465                 tag : 'div',
30466                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30467                 cn : [
30468                     {
30469                         tag : 'label',
30470                         html : this.fieldLabel
30471                     }
30472                 ]
30473             };
30474             
30475             if(this.labelAlign == 'left'){
30476             
30477                 if(this.labelWidth > 12){
30478                     label.style = "width: " + this.labelWidth + 'px';
30479                 }
30480
30481                 if(this.labelWidth < 13 && this.labelmd == 0){
30482                     this.labelmd = this.labelWidth;
30483                 }
30484
30485                 if(this.labellg > 0){
30486                     labelCls = ' col-lg-' + this.labellg;
30487                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30488                 }
30489
30490                 if(this.labelmd > 0){
30491                     labelCls = ' col-md-' + this.labelmd;
30492                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30493                 }
30494
30495                 if(this.labelsm > 0){
30496                     labelCls = ' col-sm-' + this.labelsm;
30497                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30498                 }
30499
30500                 if(this.labelxs > 0){
30501                     labelCls = ' col-xs-' + this.labelxs;
30502                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30503                 }
30504             }
30505             
30506             label.cls += ' ' + labelCls;
30507             
30508             cfg.cn.push(label);
30509         }
30510         
30511         Roo.each(['day', 'month', 'year'], function(t){
30512             cfg.cn.push({
30513                 tag : 'div',
30514                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30515             });
30516         }, this);
30517         
30518         return cfg;
30519     },
30520     
30521     inputEl: function ()
30522     {
30523         return this.el.select('.roo-date-split-field-group-value', true).first();
30524     },
30525     
30526     onRender : function(ct, position) 
30527     {
30528         var _this = this;
30529         
30530         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30531         
30532         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30533         
30534         this.dayField = new Roo.bootstrap.ComboBox({
30535             allowBlank : this.dayAllowBlank,
30536             alwaysQuery : true,
30537             displayField : 'value',
30538             editable : false,
30539             fieldLabel : '',
30540             forceSelection : true,
30541             mode : 'local',
30542             placeholder : this.dayPlaceholder,
30543             selectOnFocus : true,
30544             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30545             triggerAction : 'all',
30546             typeAhead : true,
30547             valueField : 'value',
30548             store : new Roo.data.SimpleStore({
30549                 data : (function() {    
30550                     var days = [];
30551                     _this.fireEvent('days', _this, days);
30552                     return days;
30553                 })(),
30554                 fields : [ 'value' ]
30555             }),
30556             listeners : {
30557                 select : function (_self, record, index)
30558                 {
30559                     _this.setValue(_this.getValue());
30560                 }
30561             }
30562         });
30563
30564         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30565         
30566         this.monthField = new Roo.bootstrap.MonthField({
30567             after : '<i class=\"fa fa-calendar\"></i>',
30568             allowBlank : this.monthAllowBlank,
30569             placeholder : this.monthPlaceholder,
30570             readOnly : true,
30571             listeners : {
30572                 render : function (_self)
30573                 {
30574                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30575                         e.preventDefault();
30576                         _self.focus();
30577                     });
30578                 },
30579                 select : function (_self, oldvalue, newvalue)
30580                 {
30581                     _this.setValue(_this.getValue());
30582                 }
30583             }
30584         });
30585         
30586         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30587         
30588         this.yearField = new Roo.bootstrap.ComboBox({
30589             allowBlank : this.yearAllowBlank,
30590             alwaysQuery : true,
30591             displayField : 'value',
30592             editable : false,
30593             fieldLabel : '',
30594             forceSelection : true,
30595             mode : 'local',
30596             placeholder : this.yearPlaceholder,
30597             selectOnFocus : true,
30598             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30599             triggerAction : 'all',
30600             typeAhead : true,
30601             valueField : 'value',
30602             store : new Roo.data.SimpleStore({
30603                 data : (function() {
30604                     var years = [];
30605                     _this.fireEvent('years', _this, years);
30606                     return years;
30607                 })(),
30608                 fields : [ 'value' ]
30609             }),
30610             listeners : {
30611                 select : function (_self, record, index)
30612                 {
30613                     _this.setValue(_this.getValue());
30614                 }
30615             }
30616         });
30617
30618         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30619     },
30620     
30621     setValue : function(v, format)
30622     {
30623         this.inputEl.dom.value = v;
30624         
30625         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30626         
30627         var d = Date.parseDate(v, f);
30628         
30629         if(!d){
30630             this.validate();
30631             return;
30632         }
30633         
30634         this.setDay(d.format(this.dayFormat));
30635         this.setMonth(d.format(this.monthFormat));
30636         this.setYear(d.format(this.yearFormat));
30637         
30638         this.validate();
30639         
30640         return;
30641     },
30642     
30643     setDay : function(v)
30644     {
30645         this.dayField.setValue(v);
30646         this.inputEl.dom.value = this.getValue();
30647         this.validate();
30648         return;
30649     },
30650     
30651     setMonth : function(v)
30652     {
30653         this.monthField.setValue(v, true);
30654         this.inputEl.dom.value = this.getValue();
30655         this.validate();
30656         return;
30657     },
30658     
30659     setYear : function(v)
30660     {
30661         this.yearField.setValue(v);
30662         this.inputEl.dom.value = this.getValue();
30663         this.validate();
30664         return;
30665     },
30666     
30667     getDay : function()
30668     {
30669         return this.dayField.getValue();
30670     },
30671     
30672     getMonth : function()
30673     {
30674         return this.monthField.getValue();
30675     },
30676     
30677     getYear : function()
30678     {
30679         return this.yearField.getValue();
30680     },
30681     
30682     getValue : function()
30683     {
30684         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30685         
30686         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30687         
30688         return date;
30689     },
30690     
30691     reset : function()
30692     {
30693         this.setDay('');
30694         this.setMonth('');
30695         this.setYear('');
30696         this.inputEl.dom.value = '';
30697         this.validate();
30698         return;
30699     },
30700     
30701     validate : function()
30702     {
30703         var d = this.dayField.validate();
30704         var m = this.monthField.validate();
30705         var y = this.yearField.validate();
30706         
30707         var valid = true;
30708         
30709         if(
30710                 (!this.dayAllowBlank && !d) ||
30711                 (!this.monthAllowBlank && !m) ||
30712                 (!this.yearAllowBlank && !y)
30713         ){
30714             valid = false;
30715         }
30716         
30717         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30718             return valid;
30719         }
30720         
30721         if(valid){
30722             this.markValid();
30723             return valid;
30724         }
30725         
30726         this.markInvalid();
30727         
30728         return valid;
30729     },
30730     
30731     markValid : function()
30732     {
30733         
30734         var label = this.el.select('label', true).first();
30735         var icon = this.el.select('i.fa-star', true).first();
30736
30737         if(label && icon){
30738             icon.remove();
30739         }
30740         
30741         this.fireEvent('valid', this);
30742     },
30743     
30744      /**
30745      * Mark this field as invalid
30746      * @param {String} msg The validation message
30747      */
30748     markInvalid : function(msg)
30749     {
30750         
30751         var label = this.el.select('label', true).first();
30752         var icon = this.el.select('i.fa-star', true).first();
30753
30754         if(label && !icon){
30755             this.el.select('.roo-date-split-field-label', true).createChild({
30756                 tag : 'i',
30757                 cls : 'text-danger fa fa-lg fa-star',
30758                 tooltip : 'This field is required',
30759                 style : 'margin-right:5px;'
30760             }, label, true);
30761         }
30762         
30763         this.fireEvent('invalid', this, msg);
30764     },
30765     
30766     clearInvalid : function()
30767     {
30768         var label = this.el.select('label', true).first();
30769         var icon = this.el.select('i.fa-star', true).first();
30770
30771         if(label && icon){
30772             icon.remove();
30773         }
30774         
30775         this.fireEvent('valid', this);
30776     },
30777     
30778     getName: function()
30779     {
30780         return this.name;
30781     }
30782     
30783 });
30784
30785  /**
30786  *
30787  * This is based on 
30788  * http://masonry.desandro.com
30789  *
30790  * The idea is to render all the bricks based on vertical width...
30791  *
30792  * The original code extends 'outlayer' - we might need to use that....
30793  * 
30794  */
30795
30796
30797 /**
30798  * @class Roo.bootstrap.LayoutMasonry
30799  * @extends Roo.bootstrap.Component
30800  * Bootstrap Layout Masonry class
30801  * 
30802  * @constructor
30803  * Create a new Element
30804  * @param {Object} config The config object
30805  */
30806
30807 Roo.bootstrap.LayoutMasonry = function(config){
30808     
30809     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30810     
30811     this.bricks = [];
30812     
30813     Roo.bootstrap.LayoutMasonry.register(this);
30814     
30815     this.addEvents({
30816         // raw events
30817         /**
30818          * @event layout
30819          * Fire after layout the items
30820          * @param {Roo.bootstrap.LayoutMasonry} this
30821          * @param {Roo.EventObject} e
30822          */
30823         "layout" : true
30824     });
30825     
30826 };
30827
30828 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30829     
30830     /**
30831      * @cfg {Boolean} isLayoutInstant = no animation?
30832      */   
30833     isLayoutInstant : false, // needed?
30834    
30835     /**
30836      * @cfg {Number} boxWidth  width of the columns
30837      */   
30838     boxWidth : 450,
30839     
30840       /**
30841      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30842      */   
30843     boxHeight : 0,
30844     
30845     /**
30846      * @cfg {Number} padWidth padding below box..
30847      */   
30848     padWidth : 10, 
30849     
30850     /**
30851      * @cfg {Number} gutter gutter width..
30852      */   
30853     gutter : 10,
30854     
30855      /**
30856      * @cfg {Number} maxCols maximum number of columns
30857      */   
30858     
30859     maxCols: 0,
30860     
30861     /**
30862      * @cfg {Boolean} isAutoInitial defalut true
30863      */   
30864     isAutoInitial : true, 
30865     
30866     containerWidth: 0,
30867     
30868     /**
30869      * @cfg {Boolean} isHorizontal defalut false
30870      */   
30871     isHorizontal : false, 
30872
30873     currentSize : null,
30874     
30875     tag: 'div',
30876     
30877     cls: '',
30878     
30879     bricks: null, //CompositeElement
30880     
30881     cols : 1,
30882     
30883     _isLayoutInited : false,
30884     
30885 //    isAlternative : false, // only use for vertical layout...
30886     
30887     /**
30888      * @cfg {Number} alternativePadWidth padding below box..
30889      */   
30890     alternativePadWidth : 50,
30891     
30892     selectedBrick : [],
30893     
30894     getAutoCreate : function(){
30895         
30896         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30897         
30898         var cfg = {
30899             tag: this.tag,
30900             cls: 'blog-masonary-wrapper ' + this.cls,
30901             cn : {
30902                 cls : 'mas-boxes masonary'
30903             }
30904         };
30905         
30906         return cfg;
30907     },
30908     
30909     getChildContainer: function( )
30910     {
30911         if (this.boxesEl) {
30912             return this.boxesEl;
30913         }
30914         
30915         this.boxesEl = this.el.select('.mas-boxes').first();
30916         
30917         return this.boxesEl;
30918     },
30919     
30920     
30921     initEvents : function()
30922     {
30923         var _this = this;
30924         
30925         if(this.isAutoInitial){
30926             Roo.log('hook children rendered');
30927             this.on('childrenrendered', function() {
30928                 Roo.log('children rendered');
30929                 _this.initial();
30930             } ,this);
30931         }
30932     },
30933     
30934     initial : function()
30935     {
30936         this.selectedBrick = [];
30937         
30938         this.currentSize = this.el.getBox(true);
30939         
30940         Roo.EventManager.onWindowResize(this.resize, this); 
30941
30942         if(!this.isAutoInitial){
30943             this.layout();
30944             return;
30945         }
30946         
30947         this.layout();
30948         
30949         return;
30950         //this.layout.defer(500,this);
30951         
30952     },
30953     
30954     resize : function()
30955     {
30956         var cs = this.el.getBox(true);
30957         
30958         if (
30959                 this.currentSize.width == cs.width && 
30960                 this.currentSize.x == cs.x && 
30961                 this.currentSize.height == cs.height && 
30962                 this.currentSize.y == cs.y 
30963         ) {
30964             Roo.log("no change in with or X or Y");
30965             return;
30966         }
30967         
30968         this.currentSize = cs;
30969         
30970         this.layout();
30971         
30972     },
30973     
30974     layout : function()
30975     {   
30976         this._resetLayout();
30977         
30978         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30979         
30980         this.layoutItems( isInstant );
30981       
30982         this._isLayoutInited = true;
30983         
30984         this.fireEvent('layout', this);
30985         
30986     },
30987     
30988     _resetLayout : function()
30989     {
30990         if(this.isHorizontal){
30991             this.horizontalMeasureColumns();
30992             return;
30993         }
30994         
30995         this.verticalMeasureColumns();
30996         
30997     },
30998     
30999     verticalMeasureColumns : function()
31000     {
31001         this.getContainerWidth();
31002         
31003 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31004 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31005 //            return;
31006 //        }
31007         
31008         var boxWidth = this.boxWidth + this.padWidth;
31009         
31010         if(this.containerWidth < this.boxWidth){
31011             boxWidth = this.containerWidth
31012         }
31013         
31014         var containerWidth = this.containerWidth;
31015         
31016         var cols = Math.floor(containerWidth / boxWidth);
31017         
31018         this.cols = Math.max( cols, 1 );
31019         
31020         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31021         
31022         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31023         
31024         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31025         
31026         this.colWidth = boxWidth + avail - this.padWidth;
31027         
31028         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31029         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31030     },
31031     
31032     horizontalMeasureColumns : function()
31033     {
31034         this.getContainerWidth();
31035         
31036         var boxWidth = this.boxWidth;
31037         
31038         if(this.containerWidth < boxWidth){
31039             boxWidth = this.containerWidth;
31040         }
31041         
31042         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31043         
31044         this.el.setHeight(boxWidth);
31045         
31046     },
31047     
31048     getContainerWidth : function()
31049     {
31050         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31051     },
31052     
31053     layoutItems : function( isInstant )
31054     {
31055         Roo.log(this.bricks);
31056         
31057         var items = Roo.apply([], this.bricks);
31058         
31059         if(this.isHorizontal){
31060             this._horizontalLayoutItems( items , isInstant );
31061             return;
31062         }
31063         
31064 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31065 //            this._verticalAlternativeLayoutItems( items , isInstant );
31066 //            return;
31067 //        }
31068         
31069         this._verticalLayoutItems( items , isInstant );
31070         
31071     },
31072     
31073     _verticalLayoutItems : function ( items , isInstant)
31074     {
31075         if ( !items || !items.length ) {
31076             return;
31077         }
31078         
31079         var standard = [
31080             ['xs', 'xs', 'xs', 'tall'],
31081             ['xs', 'xs', 'tall'],
31082             ['xs', 'xs', 'sm'],
31083             ['xs', 'xs', 'xs'],
31084             ['xs', 'tall'],
31085             ['xs', 'sm'],
31086             ['xs', 'xs'],
31087             ['xs'],
31088             
31089             ['sm', 'xs', 'xs'],
31090             ['sm', 'xs'],
31091             ['sm'],
31092             
31093             ['tall', 'xs', 'xs', 'xs'],
31094             ['tall', 'xs', 'xs'],
31095             ['tall', 'xs'],
31096             ['tall']
31097             
31098         ];
31099         
31100         var queue = [];
31101         
31102         var boxes = [];
31103         
31104         var box = [];
31105         
31106         Roo.each(items, function(item, k){
31107             
31108             switch (item.size) {
31109                 // these layouts take up a full box,
31110                 case 'md' :
31111                 case 'md-left' :
31112                 case 'md-right' :
31113                 case 'wide' :
31114                     
31115                     if(box.length){
31116                         boxes.push(box);
31117                         box = [];
31118                     }
31119                     
31120                     boxes.push([item]);
31121                     
31122                     break;
31123                     
31124                 case 'xs' :
31125                 case 'sm' :
31126                 case 'tall' :
31127                     
31128                     box.push(item);
31129                     
31130                     break;
31131                 default :
31132                     break;
31133                     
31134             }
31135             
31136         }, this);
31137         
31138         if(box.length){
31139             boxes.push(box);
31140             box = [];
31141         }
31142         
31143         var filterPattern = function(box, length)
31144         {
31145             if(!box.length){
31146                 return;
31147             }
31148             
31149             var match = false;
31150             
31151             var pattern = box.slice(0, length);
31152             
31153             var format = [];
31154             
31155             Roo.each(pattern, function(i){
31156                 format.push(i.size);
31157             }, this);
31158             
31159             Roo.each(standard, function(s){
31160                 
31161                 if(String(s) != String(format)){
31162                     return;
31163                 }
31164                 
31165                 match = true;
31166                 return false;
31167                 
31168             }, this);
31169             
31170             if(!match && length == 1){
31171                 return;
31172             }
31173             
31174             if(!match){
31175                 filterPattern(box, length - 1);
31176                 return;
31177             }
31178                 
31179             queue.push(pattern);
31180
31181             box = box.slice(length, box.length);
31182
31183             filterPattern(box, 4);
31184
31185             return;
31186             
31187         }
31188         
31189         Roo.each(boxes, function(box, k){
31190             
31191             if(!box.length){
31192                 return;
31193             }
31194             
31195             if(box.length == 1){
31196                 queue.push(box);
31197                 return;
31198             }
31199             
31200             filterPattern(box, 4);
31201             
31202         }, this);
31203         
31204         this._processVerticalLayoutQueue( queue, isInstant );
31205         
31206     },
31207     
31208 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31209 //    {
31210 //        if ( !items || !items.length ) {
31211 //            return;
31212 //        }
31213 //
31214 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31215 //        
31216 //    },
31217     
31218     _horizontalLayoutItems : function ( items , isInstant)
31219     {
31220         if ( !items || !items.length || items.length < 3) {
31221             return;
31222         }
31223         
31224         items.reverse();
31225         
31226         var eItems = items.slice(0, 3);
31227         
31228         items = items.slice(3, items.length);
31229         
31230         var standard = [
31231             ['xs', 'xs', 'xs', 'wide'],
31232             ['xs', 'xs', 'wide'],
31233             ['xs', 'xs', 'sm'],
31234             ['xs', 'xs', 'xs'],
31235             ['xs', 'wide'],
31236             ['xs', 'sm'],
31237             ['xs', 'xs'],
31238             ['xs'],
31239             
31240             ['sm', 'xs', 'xs'],
31241             ['sm', 'xs'],
31242             ['sm'],
31243             
31244             ['wide', 'xs', 'xs', 'xs'],
31245             ['wide', 'xs', 'xs'],
31246             ['wide', 'xs'],
31247             ['wide'],
31248             
31249             ['wide-thin']
31250         ];
31251         
31252         var queue = [];
31253         
31254         var boxes = [];
31255         
31256         var box = [];
31257         
31258         Roo.each(items, function(item, k){
31259             
31260             switch (item.size) {
31261                 case 'md' :
31262                 case 'md-left' :
31263                 case 'md-right' :
31264                 case 'tall' :
31265                     
31266                     if(box.length){
31267                         boxes.push(box);
31268                         box = [];
31269                     }
31270                     
31271                     boxes.push([item]);
31272                     
31273                     break;
31274                     
31275                 case 'xs' :
31276                 case 'sm' :
31277                 case 'wide' :
31278                 case 'wide-thin' :
31279                     
31280                     box.push(item);
31281                     
31282                     break;
31283                 default :
31284                     break;
31285                     
31286             }
31287             
31288         }, this);
31289         
31290         if(box.length){
31291             boxes.push(box);
31292             box = [];
31293         }
31294         
31295         var filterPattern = function(box, length)
31296         {
31297             if(!box.length){
31298                 return;
31299             }
31300             
31301             var match = false;
31302             
31303             var pattern = box.slice(0, length);
31304             
31305             var format = [];
31306             
31307             Roo.each(pattern, function(i){
31308                 format.push(i.size);
31309             }, this);
31310             
31311             Roo.each(standard, function(s){
31312                 
31313                 if(String(s) != String(format)){
31314                     return;
31315                 }
31316                 
31317                 match = true;
31318                 return false;
31319                 
31320             }, this);
31321             
31322             if(!match && length == 1){
31323                 return;
31324             }
31325             
31326             if(!match){
31327                 filterPattern(box, length - 1);
31328                 return;
31329             }
31330                 
31331             queue.push(pattern);
31332
31333             box = box.slice(length, box.length);
31334
31335             filterPattern(box, 4);
31336
31337             return;
31338             
31339         }
31340         
31341         Roo.each(boxes, function(box, k){
31342             
31343             if(!box.length){
31344                 return;
31345             }
31346             
31347             if(box.length == 1){
31348                 queue.push(box);
31349                 return;
31350             }
31351             
31352             filterPattern(box, 4);
31353             
31354         }, this);
31355         
31356         
31357         var prune = [];
31358         
31359         var pos = this.el.getBox(true);
31360         
31361         var minX = pos.x;
31362         
31363         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31364         
31365         var hit_end = false;
31366         
31367         Roo.each(queue, function(box){
31368             
31369             if(hit_end){
31370                 
31371                 Roo.each(box, function(b){
31372                 
31373                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31374                     b.el.hide();
31375
31376                 }, this);
31377
31378                 return;
31379             }
31380             
31381             var mx = 0;
31382             
31383             Roo.each(box, function(b){
31384                 
31385                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31386                 b.el.show();
31387
31388                 mx = Math.max(mx, b.x);
31389                 
31390             }, this);
31391             
31392             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31393             
31394             if(maxX < minX){
31395                 
31396                 Roo.each(box, function(b){
31397                 
31398                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31399                     b.el.hide();
31400                     
31401                 }, this);
31402                 
31403                 hit_end = true;
31404                 
31405                 return;
31406             }
31407             
31408             prune.push(box);
31409             
31410         }, this);
31411         
31412         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31413     },
31414     
31415     /** Sets position of item in DOM
31416     * @param {Element} item
31417     * @param {Number} x - horizontal position
31418     * @param {Number} y - vertical position
31419     * @param {Boolean} isInstant - disables transitions
31420     */
31421     _processVerticalLayoutQueue : function( queue, isInstant )
31422     {
31423         var pos = this.el.getBox(true);
31424         var x = pos.x;
31425         var y = pos.y;
31426         var maxY = [];
31427         
31428         for (var i = 0; i < this.cols; i++){
31429             maxY[i] = pos.y;
31430         }
31431         
31432         Roo.each(queue, function(box, k){
31433             
31434             var col = k % this.cols;
31435             
31436             Roo.each(box, function(b,kk){
31437                 
31438                 b.el.position('absolute');
31439                 
31440                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31441                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31442                 
31443                 if(b.size == 'md-left' || b.size == 'md-right'){
31444                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31445                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31446                 }
31447                 
31448                 b.el.setWidth(width);
31449                 b.el.setHeight(height);
31450                 // iframe?
31451                 b.el.select('iframe',true).setSize(width,height);
31452                 
31453             }, this);
31454             
31455             for (var i = 0; i < this.cols; i++){
31456                 
31457                 if(maxY[i] < maxY[col]){
31458                     col = i;
31459                     continue;
31460                 }
31461                 
31462                 col = Math.min(col, i);
31463                 
31464             }
31465             
31466             x = pos.x + col * (this.colWidth + this.padWidth);
31467             
31468             y = maxY[col];
31469             
31470             var positions = [];
31471             
31472             switch (box.length){
31473                 case 1 :
31474                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31475                     break;
31476                 case 2 :
31477                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31478                     break;
31479                 case 3 :
31480                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31481                     break;
31482                 case 4 :
31483                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31484                     break;
31485                 default :
31486                     break;
31487             }
31488             
31489             Roo.each(box, function(b,kk){
31490                 
31491                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31492                 
31493                 var sz = b.el.getSize();
31494                 
31495                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31496                 
31497             }, this);
31498             
31499         }, this);
31500         
31501         var mY = 0;
31502         
31503         for (var i = 0; i < this.cols; i++){
31504             mY = Math.max(mY, maxY[i]);
31505         }
31506         
31507         this.el.setHeight(mY - pos.y);
31508         
31509     },
31510     
31511 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31512 //    {
31513 //        var pos = this.el.getBox(true);
31514 //        var x = pos.x;
31515 //        var y = pos.y;
31516 //        var maxX = pos.right;
31517 //        
31518 //        var maxHeight = 0;
31519 //        
31520 //        Roo.each(items, function(item, k){
31521 //            
31522 //            var c = k % 2;
31523 //            
31524 //            item.el.position('absolute');
31525 //                
31526 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31527 //
31528 //            item.el.setWidth(width);
31529 //
31530 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31531 //
31532 //            item.el.setHeight(height);
31533 //            
31534 //            if(c == 0){
31535 //                item.el.setXY([x, y], isInstant ? false : true);
31536 //            } else {
31537 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31538 //            }
31539 //            
31540 //            y = y + height + this.alternativePadWidth;
31541 //            
31542 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31543 //            
31544 //        }, this);
31545 //        
31546 //        this.el.setHeight(maxHeight);
31547 //        
31548 //    },
31549     
31550     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31551     {
31552         var pos = this.el.getBox(true);
31553         
31554         var minX = pos.x;
31555         var minY = pos.y;
31556         
31557         var maxX = pos.right;
31558         
31559         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31560         
31561         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31562         
31563         Roo.each(queue, function(box, k){
31564             
31565             Roo.each(box, function(b, kk){
31566                 
31567                 b.el.position('absolute');
31568                 
31569                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31570                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31571                 
31572                 if(b.size == 'md-left' || b.size == 'md-right'){
31573                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31574                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31575                 }
31576                 
31577                 b.el.setWidth(width);
31578                 b.el.setHeight(height);
31579                 
31580             }, this);
31581             
31582             if(!box.length){
31583                 return;
31584             }
31585             
31586             var positions = [];
31587             
31588             switch (box.length){
31589                 case 1 :
31590                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31591                     break;
31592                 case 2 :
31593                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31594                     break;
31595                 case 3 :
31596                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31597                     break;
31598                 case 4 :
31599                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31600                     break;
31601                 default :
31602                     break;
31603             }
31604             
31605             Roo.each(box, function(b,kk){
31606                 
31607                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31608                 
31609                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31610                 
31611             }, this);
31612             
31613         }, this);
31614         
31615     },
31616     
31617     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31618     {
31619         Roo.each(eItems, function(b,k){
31620             
31621             b.size = (k == 0) ? 'sm' : 'xs';
31622             b.x = (k == 0) ? 2 : 1;
31623             b.y = (k == 0) ? 2 : 1;
31624             
31625             b.el.position('absolute');
31626             
31627             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31628                 
31629             b.el.setWidth(width);
31630             
31631             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31632             
31633             b.el.setHeight(height);
31634             
31635         }, this);
31636
31637         var positions = [];
31638         
31639         positions.push({
31640             x : maxX - this.unitWidth * 2 - this.gutter,
31641             y : minY
31642         });
31643         
31644         positions.push({
31645             x : maxX - this.unitWidth,
31646             y : minY + (this.unitWidth + this.gutter) * 2
31647         });
31648         
31649         positions.push({
31650             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31651             y : minY
31652         });
31653         
31654         Roo.each(eItems, function(b,k){
31655             
31656             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31657
31658         }, this);
31659         
31660     },
31661     
31662     getVerticalOneBoxColPositions : function(x, y, box)
31663     {
31664         var pos = [];
31665         
31666         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31667         
31668         if(box[0].size == 'md-left'){
31669             rand = 0;
31670         }
31671         
31672         if(box[0].size == 'md-right'){
31673             rand = 1;
31674         }
31675         
31676         pos.push({
31677             x : x + (this.unitWidth + this.gutter) * rand,
31678             y : y
31679         });
31680         
31681         return pos;
31682     },
31683     
31684     getVerticalTwoBoxColPositions : function(x, y, box)
31685     {
31686         var pos = [];
31687         
31688         if(box[0].size == 'xs'){
31689             
31690             pos.push({
31691                 x : x,
31692                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31693             });
31694
31695             pos.push({
31696                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31697                 y : y
31698             });
31699             
31700             return pos;
31701             
31702         }
31703         
31704         pos.push({
31705             x : x,
31706             y : y
31707         });
31708
31709         pos.push({
31710             x : x + (this.unitWidth + this.gutter) * 2,
31711             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31712         });
31713         
31714         return pos;
31715         
31716     },
31717     
31718     getVerticalThreeBoxColPositions : function(x, y, box)
31719     {
31720         var pos = [];
31721         
31722         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31723             
31724             pos.push({
31725                 x : x,
31726                 y : y
31727             });
31728
31729             pos.push({
31730                 x : x + (this.unitWidth + this.gutter) * 1,
31731                 y : y
31732             });
31733             
31734             pos.push({
31735                 x : x + (this.unitWidth + this.gutter) * 2,
31736                 y : y
31737             });
31738             
31739             return pos;
31740             
31741         }
31742         
31743         if(box[0].size == 'xs' && box[1].size == 'xs'){
31744             
31745             pos.push({
31746                 x : x,
31747                 y : y
31748             });
31749
31750             pos.push({
31751                 x : x,
31752                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31753             });
31754             
31755             pos.push({
31756                 x : x + (this.unitWidth + this.gutter) * 1,
31757                 y : y
31758             });
31759             
31760             return pos;
31761             
31762         }
31763         
31764         pos.push({
31765             x : x,
31766             y : y
31767         });
31768
31769         pos.push({
31770             x : x + (this.unitWidth + this.gutter) * 2,
31771             y : y
31772         });
31773
31774         pos.push({
31775             x : x + (this.unitWidth + this.gutter) * 2,
31776             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31777         });
31778             
31779         return pos;
31780         
31781     },
31782     
31783     getVerticalFourBoxColPositions : function(x, y, box)
31784     {
31785         var pos = [];
31786         
31787         if(box[0].size == 'xs'){
31788             
31789             pos.push({
31790                 x : x,
31791                 y : y
31792             });
31793
31794             pos.push({
31795                 x : x,
31796                 y : y + (this.unitHeight + this.gutter) * 1
31797             });
31798             
31799             pos.push({
31800                 x : x,
31801                 y : y + (this.unitHeight + this.gutter) * 2
31802             });
31803             
31804             pos.push({
31805                 x : x + (this.unitWidth + this.gutter) * 1,
31806                 y : y
31807             });
31808             
31809             return pos;
31810             
31811         }
31812         
31813         pos.push({
31814             x : x,
31815             y : y
31816         });
31817
31818         pos.push({
31819             x : x + (this.unitWidth + this.gutter) * 2,
31820             y : y
31821         });
31822
31823         pos.push({
31824             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31825             y : y + (this.unitHeight + this.gutter) * 1
31826         });
31827
31828         pos.push({
31829             x : x + (this.unitWidth + this.gutter) * 2,
31830             y : y + (this.unitWidth + this.gutter) * 2
31831         });
31832
31833         return pos;
31834         
31835     },
31836     
31837     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31838     {
31839         var pos = [];
31840         
31841         if(box[0].size == 'md-left'){
31842             pos.push({
31843                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31844                 y : minY
31845             });
31846             
31847             return pos;
31848         }
31849         
31850         if(box[0].size == 'md-right'){
31851             pos.push({
31852                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31853                 y : minY + (this.unitWidth + this.gutter) * 1
31854             });
31855             
31856             return pos;
31857         }
31858         
31859         var rand = Math.floor(Math.random() * (4 - box[0].y));
31860         
31861         pos.push({
31862             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31863             y : minY + (this.unitWidth + this.gutter) * rand
31864         });
31865         
31866         return pos;
31867         
31868     },
31869     
31870     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31871     {
31872         var pos = [];
31873         
31874         if(box[0].size == 'xs'){
31875             
31876             pos.push({
31877                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31878                 y : minY
31879             });
31880
31881             pos.push({
31882                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31883                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31884             });
31885             
31886             return pos;
31887             
31888         }
31889         
31890         pos.push({
31891             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31892             y : minY
31893         });
31894
31895         pos.push({
31896             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31897             y : minY + (this.unitWidth + this.gutter) * 2
31898         });
31899         
31900         return pos;
31901         
31902     },
31903     
31904     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31905     {
31906         var pos = [];
31907         
31908         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31909             
31910             pos.push({
31911                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31912                 y : minY
31913             });
31914
31915             pos.push({
31916                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31917                 y : minY + (this.unitWidth + this.gutter) * 1
31918             });
31919             
31920             pos.push({
31921                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31922                 y : minY + (this.unitWidth + this.gutter) * 2
31923             });
31924             
31925             return pos;
31926             
31927         }
31928         
31929         if(box[0].size == 'xs' && box[1].size == 'xs'){
31930             
31931             pos.push({
31932                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31933                 y : minY
31934             });
31935
31936             pos.push({
31937                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31938                 y : minY
31939             });
31940             
31941             pos.push({
31942                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31943                 y : minY + (this.unitWidth + this.gutter) * 1
31944             });
31945             
31946             return pos;
31947             
31948         }
31949         
31950         pos.push({
31951             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31952             y : minY
31953         });
31954
31955         pos.push({
31956             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31957             y : minY + (this.unitWidth + this.gutter) * 2
31958         });
31959
31960         pos.push({
31961             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31962             y : minY + (this.unitWidth + this.gutter) * 2
31963         });
31964             
31965         return pos;
31966         
31967     },
31968     
31969     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31970     {
31971         var pos = [];
31972         
31973         if(box[0].size == 'xs'){
31974             
31975             pos.push({
31976                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31977                 y : minY
31978             });
31979
31980             pos.push({
31981                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31982                 y : minY
31983             });
31984             
31985             pos.push({
31986                 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),
31987                 y : minY
31988             });
31989             
31990             pos.push({
31991                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31992                 y : minY + (this.unitWidth + this.gutter) * 1
31993             });
31994             
31995             return pos;
31996             
31997         }
31998         
31999         pos.push({
32000             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32001             y : minY
32002         });
32003         
32004         pos.push({
32005             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32006             y : minY + (this.unitWidth + this.gutter) * 2
32007         });
32008         
32009         pos.push({
32010             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32011             y : minY + (this.unitWidth + this.gutter) * 2
32012         });
32013         
32014         pos.push({
32015             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),
32016             y : minY + (this.unitWidth + this.gutter) * 2
32017         });
32018
32019         return pos;
32020         
32021     },
32022     
32023     /**
32024     * remove a Masonry Brick
32025     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32026     */
32027     removeBrick : function(brick_id)
32028     {
32029         if (!brick_id) {
32030             return;
32031         }
32032         
32033         for (var i = 0; i<this.bricks.length; i++) {
32034             if (this.bricks[i].id == brick_id) {
32035                 this.bricks.splice(i,1);
32036                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32037                 this.initial();
32038             }
32039         }
32040     },
32041     
32042     /**
32043     * adds a Masonry Brick
32044     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32045     */
32046     addBrick : function(cfg)
32047     {
32048         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32049         //this.register(cn);
32050         cn.parentId = this.id;
32051         cn.render(this.el);
32052         return cn;
32053     },
32054     
32055     /**
32056     * register a Masonry Brick
32057     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32058     */
32059     
32060     register : function(brick)
32061     {
32062         this.bricks.push(brick);
32063         brick.masonryId = this.id;
32064     },
32065     
32066     /**
32067     * clear all the Masonry Brick
32068     */
32069     clearAll : function()
32070     {
32071         this.bricks = [];
32072         //this.getChildContainer().dom.innerHTML = "";
32073         this.el.dom.innerHTML = '';
32074     },
32075     
32076     getSelected : function()
32077     {
32078         if (!this.selectedBrick) {
32079             return false;
32080         }
32081         
32082         return this.selectedBrick;
32083     }
32084 });
32085
32086 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32087     
32088     groups: {},
32089      /**
32090     * register a Masonry Layout
32091     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32092     */
32093     
32094     register : function(layout)
32095     {
32096         this.groups[layout.id] = layout;
32097     },
32098     /**
32099     * fetch a  Masonry Layout based on the masonry layout ID
32100     * @param {string} the masonry layout to add
32101     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32102     */
32103     
32104     get: function(layout_id) {
32105         if (typeof(this.groups[layout_id]) == 'undefined') {
32106             return false;
32107         }
32108         return this.groups[layout_id] ;
32109     }
32110     
32111     
32112     
32113 });
32114
32115  
32116
32117  /**
32118  *
32119  * This is based on 
32120  * http://masonry.desandro.com
32121  *
32122  * The idea is to render all the bricks based on vertical width...
32123  *
32124  * The original code extends 'outlayer' - we might need to use that....
32125  * 
32126  */
32127
32128
32129 /**
32130  * @class Roo.bootstrap.LayoutMasonryAuto
32131  * @extends Roo.bootstrap.Component
32132  * Bootstrap Layout Masonry class
32133  * 
32134  * @constructor
32135  * Create a new Element
32136  * @param {Object} config The config object
32137  */
32138
32139 Roo.bootstrap.LayoutMasonryAuto = function(config){
32140     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32141 };
32142
32143 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32144     
32145       /**
32146      * @cfg {Boolean} isFitWidth  - resize the width..
32147      */   
32148     isFitWidth : false,  // options..
32149     /**
32150      * @cfg {Boolean} isOriginLeft = left align?
32151      */   
32152     isOriginLeft : true,
32153     /**
32154      * @cfg {Boolean} isOriginTop = top align?
32155      */   
32156     isOriginTop : false,
32157     /**
32158      * @cfg {Boolean} isLayoutInstant = no animation?
32159      */   
32160     isLayoutInstant : false, // needed?
32161     /**
32162      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32163      */   
32164     isResizingContainer : true,
32165     /**
32166      * @cfg {Number} columnWidth  width of the columns 
32167      */   
32168     
32169     columnWidth : 0,
32170     
32171     /**
32172      * @cfg {Number} maxCols maximum number of columns
32173      */   
32174     
32175     maxCols: 0,
32176     /**
32177      * @cfg {Number} padHeight padding below box..
32178      */   
32179     
32180     padHeight : 10, 
32181     
32182     /**
32183      * @cfg {Boolean} isAutoInitial defalut true
32184      */   
32185     
32186     isAutoInitial : true, 
32187     
32188     // private?
32189     gutter : 0,
32190     
32191     containerWidth: 0,
32192     initialColumnWidth : 0,
32193     currentSize : null,
32194     
32195     colYs : null, // array.
32196     maxY : 0,
32197     padWidth: 10,
32198     
32199     
32200     tag: 'div',
32201     cls: '',
32202     bricks: null, //CompositeElement
32203     cols : 0, // array?
32204     // element : null, // wrapped now this.el
32205     _isLayoutInited : null, 
32206     
32207     
32208     getAutoCreate : function(){
32209         
32210         var cfg = {
32211             tag: this.tag,
32212             cls: 'blog-masonary-wrapper ' + this.cls,
32213             cn : {
32214                 cls : 'mas-boxes masonary'
32215             }
32216         };
32217         
32218         return cfg;
32219     },
32220     
32221     getChildContainer: function( )
32222     {
32223         if (this.boxesEl) {
32224             return this.boxesEl;
32225         }
32226         
32227         this.boxesEl = this.el.select('.mas-boxes').first();
32228         
32229         return this.boxesEl;
32230     },
32231     
32232     
32233     initEvents : function()
32234     {
32235         var _this = this;
32236         
32237         if(this.isAutoInitial){
32238             Roo.log('hook children rendered');
32239             this.on('childrenrendered', function() {
32240                 Roo.log('children rendered');
32241                 _this.initial();
32242             } ,this);
32243         }
32244         
32245     },
32246     
32247     initial : function()
32248     {
32249         this.reloadItems();
32250
32251         this.currentSize = this.el.getBox(true);
32252
32253         /// was window resize... - let's see if this works..
32254         Roo.EventManager.onWindowResize(this.resize, this); 
32255
32256         if(!this.isAutoInitial){
32257             this.layout();
32258             return;
32259         }
32260         
32261         this.layout.defer(500,this);
32262     },
32263     
32264     reloadItems: function()
32265     {
32266         this.bricks = this.el.select('.masonry-brick', true);
32267         
32268         this.bricks.each(function(b) {
32269             //Roo.log(b.getSize());
32270             if (!b.attr('originalwidth')) {
32271                 b.attr('originalwidth',  b.getSize().width);
32272             }
32273             
32274         });
32275         
32276         Roo.log(this.bricks.elements.length);
32277     },
32278     
32279     resize : function()
32280     {
32281         Roo.log('resize');
32282         var cs = this.el.getBox(true);
32283         
32284         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32285             Roo.log("no change in with or X");
32286             return;
32287         }
32288         this.currentSize = cs;
32289         this.layout();
32290     },
32291     
32292     layout : function()
32293     {
32294          Roo.log('layout');
32295         this._resetLayout();
32296         //this._manageStamps();
32297       
32298         // don't animate first layout
32299         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32300         this.layoutItems( isInstant );
32301       
32302         // flag for initalized
32303         this._isLayoutInited = true;
32304     },
32305     
32306     layoutItems : function( isInstant )
32307     {
32308         //var items = this._getItemsForLayout( this.items );
32309         // original code supports filtering layout items.. we just ignore it..
32310         
32311         this._layoutItems( this.bricks , isInstant );
32312       
32313         this._postLayout();
32314     },
32315     _layoutItems : function ( items , isInstant)
32316     {
32317        //this.fireEvent( 'layout', this, items );
32318     
32319
32320         if ( !items || !items.elements.length ) {
32321           // no items, emit event with empty array
32322             return;
32323         }
32324
32325         var queue = [];
32326         items.each(function(item) {
32327             Roo.log("layout item");
32328             Roo.log(item);
32329             // get x/y object from method
32330             var position = this._getItemLayoutPosition( item );
32331             // enqueue
32332             position.item = item;
32333             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32334             queue.push( position );
32335         }, this);
32336       
32337         this._processLayoutQueue( queue );
32338     },
32339     /** Sets position of item in DOM
32340     * @param {Element} item
32341     * @param {Number} x - horizontal position
32342     * @param {Number} y - vertical position
32343     * @param {Boolean} isInstant - disables transitions
32344     */
32345     _processLayoutQueue : function( queue )
32346     {
32347         for ( var i=0, len = queue.length; i < len; i++ ) {
32348             var obj = queue[i];
32349             obj.item.position('absolute');
32350             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32351         }
32352     },
32353       
32354     
32355     /**
32356     * Any logic you want to do after each layout,
32357     * i.e. size the container
32358     */
32359     _postLayout : function()
32360     {
32361         this.resizeContainer();
32362     },
32363     
32364     resizeContainer : function()
32365     {
32366         if ( !this.isResizingContainer ) {
32367             return;
32368         }
32369         var size = this._getContainerSize();
32370         if ( size ) {
32371             this.el.setSize(size.width,size.height);
32372             this.boxesEl.setSize(size.width,size.height);
32373         }
32374     },
32375     
32376     
32377     
32378     _resetLayout : function()
32379     {
32380         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32381         this.colWidth = this.el.getWidth();
32382         //this.gutter = this.el.getWidth(); 
32383         
32384         this.measureColumns();
32385
32386         // reset column Y
32387         var i = this.cols;
32388         this.colYs = [];
32389         while (i--) {
32390             this.colYs.push( 0 );
32391         }
32392     
32393         this.maxY = 0;
32394     },
32395
32396     measureColumns : function()
32397     {
32398         this.getContainerWidth();
32399       // if columnWidth is 0, default to outerWidth of first item
32400         if ( !this.columnWidth ) {
32401             var firstItem = this.bricks.first();
32402             Roo.log(firstItem);
32403             this.columnWidth  = this.containerWidth;
32404             if (firstItem && firstItem.attr('originalwidth') ) {
32405                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32406             }
32407             // columnWidth fall back to item of first element
32408             Roo.log("set column width?");
32409                         this.initialColumnWidth = this.columnWidth  ;
32410
32411             // if first elem has no width, default to size of container
32412             
32413         }
32414         
32415         
32416         if (this.initialColumnWidth) {
32417             this.columnWidth = this.initialColumnWidth;
32418         }
32419         
32420         
32421             
32422         // column width is fixed at the top - however if container width get's smaller we should
32423         // reduce it...
32424         
32425         // this bit calcs how man columns..
32426             
32427         var columnWidth = this.columnWidth += this.gutter;
32428       
32429         // calculate columns
32430         var containerWidth = this.containerWidth + this.gutter;
32431         
32432         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32433         // fix rounding errors, typically with gutters
32434         var excess = columnWidth - containerWidth % columnWidth;
32435         
32436         
32437         // if overshoot is less than a pixel, round up, otherwise floor it
32438         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32439         cols = Math[ mathMethod ]( cols );
32440         this.cols = Math.max( cols, 1 );
32441         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32442         
32443          // padding positioning..
32444         var totalColWidth = this.cols * this.columnWidth;
32445         var padavail = this.containerWidth - totalColWidth;
32446         // so for 2 columns - we need 3 'pads'
32447         
32448         var padNeeded = (1+this.cols) * this.padWidth;
32449         
32450         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32451         
32452         this.columnWidth += padExtra
32453         //this.padWidth = Math.floor(padavail /  ( this.cols));
32454         
32455         // adjust colum width so that padding is fixed??
32456         
32457         // we have 3 columns ... total = width * 3
32458         // we have X left over... that should be used by 
32459         
32460         //if (this.expandC) {
32461             
32462         //}
32463         
32464         
32465         
32466     },
32467     
32468     getContainerWidth : function()
32469     {
32470        /* // container is parent if fit width
32471         var container = this.isFitWidth ? this.element.parentNode : this.element;
32472         // check that this.size and size are there
32473         // IE8 triggers resize on body size change, so they might not be
32474         
32475         var size = getSize( container );  //FIXME
32476         this.containerWidth = size && size.innerWidth; //FIXME
32477         */
32478          
32479         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32480         
32481     },
32482     
32483     _getItemLayoutPosition : function( item )  // what is item?
32484     {
32485         // we resize the item to our columnWidth..
32486       
32487         item.setWidth(this.columnWidth);
32488         item.autoBoxAdjust  = false;
32489         
32490         var sz = item.getSize();
32491  
32492         // how many columns does this brick span
32493         var remainder = this.containerWidth % this.columnWidth;
32494         
32495         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32496         // round if off by 1 pixel, otherwise use ceil
32497         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32498         colSpan = Math.min( colSpan, this.cols );
32499         
32500         // normally this should be '1' as we dont' currently allow multi width columns..
32501         
32502         var colGroup = this._getColGroup( colSpan );
32503         // get the minimum Y value from the columns
32504         var minimumY = Math.min.apply( Math, colGroup );
32505         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32506         
32507         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32508          
32509         // position the brick
32510         var position = {
32511             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32512             y: this.currentSize.y + minimumY + this.padHeight
32513         };
32514         
32515         Roo.log(position);
32516         // apply setHeight to necessary columns
32517         var setHeight = minimumY + sz.height + this.padHeight;
32518         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32519         
32520         var setSpan = this.cols + 1 - colGroup.length;
32521         for ( var i = 0; i < setSpan; i++ ) {
32522           this.colYs[ shortColIndex + i ] = setHeight ;
32523         }
32524       
32525         return position;
32526     },
32527     
32528     /**
32529      * @param {Number} colSpan - number of columns the element spans
32530      * @returns {Array} colGroup
32531      */
32532     _getColGroup : function( colSpan )
32533     {
32534         if ( colSpan < 2 ) {
32535           // if brick spans only one column, use all the column Ys
32536           return this.colYs;
32537         }
32538       
32539         var colGroup = [];
32540         // how many different places could this brick fit horizontally
32541         var groupCount = this.cols + 1 - colSpan;
32542         // for each group potential horizontal position
32543         for ( var i = 0; i < groupCount; i++ ) {
32544           // make an array of colY values for that one group
32545           var groupColYs = this.colYs.slice( i, i + colSpan );
32546           // and get the max value of the array
32547           colGroup[i] = Math.max.apply( Math, groupColYs );
32548         }
32549         return colGroup;
32550     },
32551     /*
32552     _manageStamp : function( stamp )
32553     {
32554         var stampSize =  stamp.getSize();
32555         var offset = stamp.getBox();
32556         // get the columns that this stamp affects
32557         var firstX = this.isOriginLeft ? offset.x : offset.right;
32558         var lastX = firstX + stampSize.width;
32559         var firstCol = Math.floor( firstX / this.columnWidth );
32560         firstCol = Math.max( 0, firstCol );
32561         
32562         var lastCol = Math.floor( lastX / this.columnWidth );
32563         // lastCol should not go over if multiple of columnWidth #425
32564         lastCol -= lastX % this.columnWidth ? 0 : 1;
32565         lastCol = Math.min( this.cols - 1, lastCol );
32566         
32567         // set colYs to bottom of the stamp
32568         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32569             stampSize.height;
32570             
32571         for ( var i = firstCol; i <= lastCol; i++ ) {
32572           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32573         }
32574     },
32575     */
32576     
32577     _getContainerSize : function()
32578     {
32579         this.maxY = Math.max.apply( Math, this.colYs );
32580         var size = {
32581             height: this.maxY
32582         };
32583       
32584         if ( this.isFitWidth ) {
32585             size.width = this._getContainerFitWidth();
32586         }
32587       
32588         return size;
32589     },
32590     
32591     _getContainerFitWidth : function()
32592     {
32593         var unusedCols = 0;
32594         // count unused columns
32595         var i = this.cols;
32596         while ( --i ) {
32597           if ( this.colYs[i] !== 0 ) {
32598             break;
32599           }
32600           unusedCols++;
32601         }
32602         // fit container to columns that have been used
32603         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32604     },
32605     
32606     needsResizeLayout : function()
32607     {
32608         var previousWidth = this.containerWidth;
32609         this.getContainerWidth();
32610         return previousWidth !== this.containerWidth;
32611     }
32612  
32613 });
32614
32615  
32616
32617  /*
32618  * - LGPL
32619  *
32620  * element
32621  * 
32622  */
32623
32624 /**
32625  * @class Roo.bootstrap.MasonryBrick
32626  * @extends Roo.bootstrap.Component
32627  * Bootstrap MasonryBrick class
32628  * 
32629  * @constructor
32630  * Create a new MasonryBrick
32631  * @param {Object} config The config object
32632  */
32633
32634 Roo.bootstrap.MasonryBrick = function(config){
32635     
32636     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32637     
32638     Roo.bootstrap.MasonryBrick.register(this);
32639     
32640     this.addEvents({
32641         // raw events
32642         /**
32643          * @event click
32644          * When a MasonryBrick is clcik
32645          * @param {Roo.bootstrap.MasonryBrick} this
32646          * @param {Roo.EventObject} e
32647          */
32648         "click" : true
32649     });
32650 };
32651
32652 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32653     
32654     /**
32655      * @cfg {String} title
32656      */   
32657     title : '',
32658     /**
32659      * @cfg {String} html
32660      */   
32661     html : '',
32662     /**
32663      * @cfg {String} bgimage
32664      */   
32665     bgimage : '',
32666     /**
32667      * @cfg {String} videourl
32668      */   
32669     videourl : '',
32670     /**
32671      * @cfg {String} cls
32672      */   
32673     cls : '',
32674     /**
32675      * @cfg {String} href
32676      */   
32677     href : '',
32678     /**
32679      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32680      */   
32681     size : 'xs',
32682     
32683     /**
32684      * @cfg {String} placetitle (center|bottom)
32685      */   
32686     placetitle : '',
32687     
32688     /**
32689      * @cfg {Boolean} isFitContainer defalut true
32690      */   
32691     isFitContainer : true, 
32692     
32693     /**
32694      * @cfg {Boolean} preventDefault defalut false
32695      */   
32696     preventDefault : false, 
32697     
32698     /**
32699      * @cfg {Boolean} inverse defalut false
32700      */   
32701     maskInverse : false, 
32702     
32703     getAutoCreate : function()
32704     {
32705         if(!this.isFitContainer){
32706             return this.getSplitAutoCreate();
32707         }
32708         
32709         var cls = 'masonry-brick masonry-brick-full';
32710         
32711         if(this.href.length){
32712             cls += ' masonry-brick-link';
32713         }
32714         
32715         if(this.bgimage.length){
32716             cls += ' masonry-brick-image';
32717         }
32718         
32719         if(this.maskInverse){
32720             cls += ' mask-inverse';
32721         }
32722         
32723         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32724             cls += ' enable-mask';
32725         }
32726         
32727         if(this.size){
32728             cls += ' masonry-' + this.size + '-brick';
32729         }
32730         
32731         if(this.placetitle.length){
32732             
32733             switch (this.placetitle) {
32734                 case 'center' :
32735                     cls += ' masonry-center-title';
32736                     break;
32737                 case 'bottom' :
32738                     cls += ' masonry-bottom-title';
32739                     break;
32740                 default:
32741                     break;
32742             }
32743             
32744         } else {
32745             if(!this.html.length && !this.bgimage.length){
32746                 cls += ' masonry-center-title';
32747             }
32748
32749             if(!this.html.length && this.bgimage.length){
32750                 cls += ' masonry-bottom-title';
32751             }
32752         }
32753         
32754         if(this.cls){
32755             cls += ' ' + this.cls;
32756         }
32757         
32758         var cfg = {
32759             tag: (this.href.length) ? 'a' : 'div',
32760             cls: cls,
32761             cn: [
32762                 {
32763                     tag: 'div',
32764                     cls: 'masonry-brick-mask'
32765                 },
32766                 {
32767                     tag: 'div',
32768                     cls: 'masonry-brick-paragraph',
32769                     cn: []
32770                 }
32771             ]
32772         };
32773         
32774         if(this.href.length){
32775             cfg.href = this.href;
32776         }
32777         
32778         var cn = cfg.cn[1].cn;
32779         
32780         if(this.title.length){
32781             cn.push({
32782                 tag: 'h4',
32783                 cls: 'masonry-brick-title',
32784                 html: this.title
32785             });
32786         }
32787         
32788         if(this.html.length){
32789             cn.push({
32790                 tag: 'p',
32791                 cls: 'masonry-brick-text',
32792                 html: this.html
32793             });
32794         }
32795         
32796         if (!this.title.length && !this.html.length) {
32797             cfg.cn[1].cls += ' hide';
32798         }
32799         
32800         if(this.bgimage.length){
32801             cfg.cn.push({
32802                 tag: 'img',
32803                 cls: 'masonry-brick-image-view',
32804                 src: this.bgimage
32805             });
32806         }
32807         
32808         if(this.videourl.length){
32809             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32810             // youtube support only?
32811             cfg.cn.push({
32812                 tag: 'iframe',
32813                 cls: 'masonry-brick-image-view',
32814                 src: vurl,
32815                 frameborder : 0,
32816                 allowfullscreen : true
32817             });
32818         }
32819         
32820         return cfg;
32821         
32822     },
32823     
32824     getSplitAutoCreate : function()
32825     {
32826         var cls = 'masonry-brick masonry-brick-split';
32827         
32828         if(this.href.length){
32829             cls += ' masonry-brick-link';
32830         }
32831         
32832         if(this.bgimage.length){
32833             cls += ' masonry-brick-image';
32834         }
32835         
32836         if(this.size){
32837             cls += ' masonry-' + this.size + '-brick';
32838         }
32839         
32840         switch (this.placetitle) {
32841             case 'center' :
32842                 cls += ' masonry-center-title';
32843                 break;
32844             case 'bottom' :
32845                 cls += ' masonry-bottom-title';
32846                 break;
32847             default:
32848                 if(!this.bgimage.length){
32849                     cls += ' masonry-center-title';
32850                 }
32851
32852                 if(this.bgimage.length){
32853                     cls += ' masonry-bottom-title';
32854                 }
32855                 break;
32856         }
32857         
32858         if(this.cls){
32859             cls += ' ' + this.cls;
32860         }
32861         
32862         var cfg = {
32863             tag: (this.href.length) ? 'a' : 'div',
32864             cls: cls,
32865             cn: [
32866                 {
32867                     tag: 'div',
32868                     cls: 'masonry-brick-split-head',
32869                     cn: [
32870                         {
32871                             tag: 'div',
32872                             cls: 'masonry-brick-paragraph',
32873                             cn: []
32874                         }
32875                     ]
32876                 },
32877                 {
32878                     tag: 'div',
32879                     cls: 'masonry-brick-split-body',
32880                     cn: []
32881                 }
32882             ]
32883         };
32884         
32885         if(this.href.length){
32886             cfg.href = this.href;
32887         }
32888         
32889         if(this.title.length){
32890             cfg.cn[0].cn[0].cn.push({
32891                 tag: 'h4',
32892                 cls: 'masonry-brick-title',
32893                 html: this.title
32894             });
32895         }
32896         
32897         if(this.html.length){
32898             cfg.cn[1].cn.push({
32899                 tag: 'p',
32900                 cls: 'masonry-brick-text',
32901                 html: this.html
32902             });
32903         }
32904
32905         if(this.bgimage.length){
32906             cfg.cn[0].cn.push({
32907                 tag: 'img',
32908                 cls: 'masonry-brick-image-view',
32909                 src: this.bgimage
32910             });
32911         }
32912         
32913         if(this.videourl.length){
32914             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32915             // youtube support only?
32916             cfg.cn[0].cn.cn.push({
32917                 tag: 'iframe',
32918                 cls: 'masonry-brick-image-view',
32919                 src: vurl,
32920                 frameborder : 0,
32921                 allowfullscreen : true
32922             });
32923         }
32924         
32925         return cfg;
32926     },
32927     
32928     initEvents: function() 
32929     {
32930         switch (this.size) {
32931             case 'xs' :
32932                 this.x = 1;
32933                 this.y = 1;
32934                 break;
32935             case 'sm' :
32936                 this.x = 2;
32937                 this.y = 2;
32938                 break;
32939             case 'md' :
32940             case 'md-left' :
32941             case 'md-right' :
32942                 this.x = 3;
32943                 this.y = 3;
32944                 break;
32945             case 'tall' :
32946                 this.x = 2;
32947                 this.y = 3;
32948                 break;
32949             case 'wide' :
32950                 this.x = 3;
32951                 this.y = 2;
32952                 break;
32953             case 'wide-thin' :
32954                 this.x = 3;
32955                 this.y = 1;
32956                 break;
32957                         
32958             default :
32959                 break;
32960         }
32961         
32962         if(Roo.isTouch){
32963             this.el.on('touchstart', this.onTouchStart, this);
32964             this.el.on('touchmove', this.onTouchMove, this);
32965             this.el.on('touchend', this.onTouchEnd, this);
32966             this.el.on('contextmenu', this.onContextMenu, this);
32967         } else {
32968             this.el.on('mouseenter'  ,this.enter, this);
32969             this.el.on('mouseleave', this.leave, this);
32970             this.el.on('click', this.onClick, this);
32971         }
32972         
32973         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32974             this.parent().bricks.push(this);   
32975         }
32976         
32977     },
32978     
32979     onClick: function(e, el)
32980     {
32981         var time = this.endTimer - this.startTimer;
32982         // Roo.log(e.preventDefault());
32983         if(Roo.isTouch){
32984             if(time > 1000){
32985                 e.preventDefault();
32986                 return;
32987             }
32988         }
32989         
32990         if(!this.preventDefault){
32991             return;
32992         }
32993         
32994         e.preventDefault();
32995         
32996         if (this.activeClass != '') {
32997             this.selectBrick();
32998         }
32999         
33000         this.fireEvent('click', this, e);
33001     },
33002     
33003     enter: function(e, el)
33004     {
33005         e.preventDefault();
33006         
33007         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33008             return;
33009         }
33010         
33011         if(this.bgimage.length && this.html.length){
33012             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33013         }
33014     },
33015     
33016     leave: function(e, el)
33017     {
33018         e.preventDefault();
33019         
33020         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33021             return;
33022         }
33023         
33024         if(this.bgimage.length && this.html.length){
33025             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33026         }
33027     },
33028     
33029     onTouchStart: function(e, el)
33030     {
33031 //        e.preventDefault();
33032         
33033         this.touchmoved = false;
33034         
33035         if(!this.isFitContainer){
33036             return;
33037         }
33038         
33039         if(!this.bgimage.length || !this.html.length){
33040             return;
33041         }
33042         
33043         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33044         
33045         this.timer = new Date().getTime();
33046         
33047     },
33048     
33049     onTouchMove: function(e, el)
33050     {
33051         this.touchmoved = true;
33052     },
33053     
33054     onContextMenu : function(e,el)
33055     {
33056         e.preventDefault();
33057         e.stopPropagation();
33058         return false;
33059     },
33060     
33061     onTouchEnd: function(e, el)
33062     {
33063 //        e.preventDefault();
33064         
33065         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33066         
33067             this.leave(e,el);
33068             
33069             return;
33070         }
33071         
33072         if(!this.bgimage.length || !this.html.length){
33073             
33074             if(this.href.length){
33075                 window.location.href = this.href;
33076             }
33077             
33078             return;
33079         }
33080         
33081         if(!this.isFitContainer){
33082             return;
33083         }
33084         
33085         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33086         
33087         window.location.href = this.href;
33088     },
33089     
33090     //selection on single brick only
33091     selectBrick : function() {
33092         
33093         if (!this.parentId) {
33094             return;
33095         }
33096         
33097         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33098         var index = m.selectedBrick.indexOf(this.id);
33099         
33100         if ( index > -1) {
33101             m.selectedBrick.splice(index,1);
33102             this.el.removeClass(this.activeClass);
33103             return;
33104         }
33105         
33106         for(var i = 0; i < m.selectedBrick.length; i++) {
33107             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33108             b.el.removeClass(b.activeClass);
33109         }
33110         
33111         m.selectedBrick = [];
33112         
33113         m.selectedBrick.push(this.id);
33114         this.el.addClass(this.activeClass);
33115         return;
33116     },
33117     
33118     isSelected : function(){
33119         return this.el.hasClass(this.activeClass);
33120         
33121     }
33122 });
33123
33124 Roo.apply(Roo.bootstrap.MasonryBrick, {
33125     
33126     //groups: {},
33127     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33128      /**
33129     * register a Masonry Brick
33130     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33131     */
33132     
33133     register : function(brick)
33134     {
33135         //this.groups[brick.id] = brick;
33136         this.groups.add(brick.id, brick);
33137     },
33138     /**
33139     * fetch a  masonry brick based on the masonry brick ID
33140     * @param {string} the masonry brick to add
33141     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33142     */
33143     
33144     get: function(brick_id) 
33145     {
33146         // if (typeof(this.groups[brick_id]) == 'undefined') {
33147         //     return false;
33148         // }
33149         // return this.groups[brick_id] ;
33150         
33151         if(this.groups.key(brick_id)) {
33152             return this.groups.key(brick_id);
33153         }
33154         
33155         return false;
33156     }
33157     
33158     
33159     
33160 });
33161
33162  /*
33163  * - LGPL
33164  *
33165  * element
33166  * 
33167  */
33168
33169 /**
33170  * @class Roo.bootstrap.Brick
33171  * @extends Roo.bootstrap.Component
33172  * Bootstrap Brick class
33173  * 
33174  * @constructor
33175  * Create a new Brick
33176  * @param {Object} config The config object
33177  */
33178
33179 Roo.bootstrap.Brick = function(config){
33180     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33181     
33182     this.addEvents({
33183         // raw events
33184         /**
33185          * @event click
33186          * When a Brick is click
33187          * @param {Roo.bootstrap.Brick} this
33188          * @param {Roo.EventObject} e
33189          */
33190         "click" : true
33191     });
33192 };
33193
33194 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33195     
33196     /**
33197      * @cfg {String} title
33198      */   
33199     title : '',
33200     /**
33201      * @cfg {String} html
33202      */   
33203     html : '',
33204     /**
33205      * @cfg {String} bgimage
33206      */   
33207     bgimage : '',
33208     /**
33209      * @cfg {String} cls
33210      */   
33211     cls : '',
33212     /**
33213      * @cfg {String} href
33214      */   
33215     href : '',
33216     /**
33217      * @cfg {String} video
33218      */   
33219     video : '',
33220     /**
33221      * @cfg {Boolean} square
33222      */   
33223     square : true,
33224     
33225     getAutoCreate : function()
33226     {
33227         var cls = 'roo-brick';
33228         
33229         if(this.href.length){
33230             cls += ' roo-brick-link';
33231         }
33232         
33233         if(this.bgimage.length){
33234             cls += ' roo-brick-image';
33235         }
33236         
33237         if(!this.html.length && !this.bgimage.length){
33238             cls += ' roo-brick-center-title';
33239         }
33240         
33241         if(!this.html.length && this.bgimage.length){
33242             cls += ' roo-brick-bottom-title';
33243         }
33244         
33245         if(this.cls){
33246             cls += ' ' + this.cls;
33247         }
33248         
33249         var cfg = {
33250             tag: (this.href.length) ? 'a' : 'div',
33251             cls: cls,
33252             cn: [
33253                 {
33254                     tag: 'div',
33255                     cls: 'roo-brick-paragraph',
33256                     cn: []
33257                 }
33258             ]
33259         };
33260         
33261         if(this.href.length){
33262             cfg.href = this.href;
33263         }
33264         
33265         var cn = cfg.cn[0].cn;
33266         
33267         if(this.title.length){
33268             cn.push({
33269                 tag: 'h4',
33270                 cls: 'roo-brick-title',
33271                 html: this.title
33272             });
33273         }
33274         
33275         if(this.html.length){
33276             cn.push({
33277                 tag: 'p',
33278                 cls: 'roo-brick-text',
33279                 html: this.html
33280             });
33281         } else {
33282             cn.cls += ' hide';
33283         }
33284         
33285         if(this.bgimage.length){
33286             cfg.cn.push({
33287                 tag: 'img',
33288                 cls: 'roo-brick-image-view',
33289                 src: this.bgimage
33290             });
33291         }
33292         
33293         return cfg;
33294     },
33295     
33296     initEvents: function() 
33297     {
33298         if(this.title.length || this.html.length){
33299             this.el.on('mouseenter'  ,this.enter, this);
33300             this.el.on('mouseleave', this.leave, this);
33301         }
33302         
33303         Roo.EventManager.onWindowResize(this.resize, this); 
33304         
33305         if(this.bgimage.length){
33306             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33307             this.imageEl.on('load', this.onImageLoad, this);
33308             return;
33309         }
33310         
33311         this.resize();
33312     },
33313     
33314     onImageLoad : function()
33315     {
33316         this.resize();
33317     },
33318     
33319     resize : function()
33320     {
33321         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33322         
33323         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33324         
33325         if(this.bgimage.length){
33326             var image = this.el.select('.roo-brick-image-view', true).first();
33327             
33328             image.setWidth(paragraph.getWidth());
33329             
33330             if(this.square){
33331                 image.setHeight(paragraph.getWidth());
33332             }
33333             
33334             this.el.setHeight(image.getHeight());
33335             paragraph.setHeight(image.getHeight());
33336             
33337         }
33338         
33339     },
33340     
33341     enter: function(e, el)
33342     {
33343         e.preventDefault();
33344         
33345         if(this.bgimage.length){
33346             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33347             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33348         }
33349     },
33350     
33351     leave: function(e, el)
33352     {
33353         e.preventDefault();
33354         
33355         if(this.bgimage.length){
33356             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33357             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33358         }
33359     }
33360     
33361 });
33362
33363  
33364
33365  /*
33366  * - LGPL
33367  *
33368  * Number field 
33369  */
33370
33371 /**
33372  * @class Roo.bootstrap.NumberField
33373  * @extends Roo.bootstrap.Input
33374  * Bootstrap NumberField class
33375  * 
33376  * 
33377  * 
33378  * 
33379  * @constructor
33380  * Create a new NumberField
33381  * @param {Object} config The config object
33382  */
33383
33384 Roo.bootstrap.NumberField = function(config){
33385     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33386 };
33387
33388 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33389     
33390     /**
33391      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33392      */
33393     allowDecimals : true,
33394     /**
33395      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33396      */
33397     decimalSeparator : ".",
33398     /**
33399      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33400      */
33401     decimalPrecision : 2,
33402     /**
33403      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33404      */
33405     allowNegative : true,
33406     
33407     /**
33408      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33409      */
33410     allowZero: true,
33411     /**
33412      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33413      */
33414     minValue : Number.NEGATIVE_INFINITY,
33415     /**
33416      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33417      */
33418     maxValue : Number.MAX_VALUE,
33419     /**
33420      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33421      */
33422     minText : "The minimum value for this field is {0}",
33423     /**
33424      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33425      */
33426     maxText : "The maximum value for this field is {0}",
33427     /**
33428      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33429      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33430      */
33431     nanText : "{0} is not a valid number",
33432     /**
33433      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33434      */
33435     thousandsDelimiter : false,
33436     /**
33437      * @cfg {String} valueAlign alignment of value
33438      */
33439     valueAlign : "left",
33440
33441     getAutoCreate : function()
33442     {
33443         var hiddenInput = {
33444             tag: 'input',
33445             type: 'hidden',
33446             id: Roo.id(),
33447             cls: 'hidden-number-input'
33448         };
33449         
33450         if (this.name) {
33451             hiddenInput.name = this.name;
33452         }
33453         
33454         this.name = '';
33455         
33456         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33457         
33458         this.name = hiddenInput.name;
33459         
33460         if(cfg.cn.length > 0) {
33461             cfg.cn.push(hiddenInput);
33462         }
33463         
33464         return cfg;
33465     },
33466
33467     // private
33468     initEvents : function()
33469     {   
33470         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33471         
33472         var allowed = "0123456789";
33473         
33474         if(this.allowDecimals){
33475             allowed += this.decimalSeparator;
33476         }
33477         
33478         if(this.allowNegative){
33479             allowed += "-";
33480         }
33481         
33482         if(this.thousandsDelimiter) {
33483             allowed += ",";
33484         }
33485         
33486         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33487         
33488         var keyPress = function(e){
33489             
33490             var k = e.getKey();
33491             
33492             var c = e.getCharCode();
33493             
33494             if(
33495                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33496                     allowed.indexOf(String.fromCharCode(c)) === -1
33497             ){
33498                 e.stopEvent();
33499                 return;
33500             }
33501             
33502             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33503                 return;
33504             }
33505             
33506             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33507                 e.stopEvent();
33508             }
33509         };
33510         
33511         this.el.on("keypress", keyPress, this);
33512     },
33513     
33514     validateValue : function(value)
33515     {
33516         
33517         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33518             return false;
33519         }
33520         
33521         var num = this.parseValue(value);
33522         
33523         if(isNaN(num)){
33524             this.markInvalid(String.format(this.nanText, value));
33525             return false;
33526         }
33527         
33528         if(num < this.minValue){
33529             this.markInvalid(String.format(this.minText, this.minValue));
33530             return false;
33531         }
33532         
33533         if(num > this.maxValue){
33534             this.markInvalid(String.format(this.maxText, this.maxValue));
33535             return false;
33536         }
33537         
33538         return true;
33539     },
33540
33541     getValue : function()
33542     {
33543         var v = this.hiddenEl().getValue();
33544         
33545         return this.fixPrecision(this.parseValue(v));
33546     },
33547
33548     parseValue : function(value)
33549     {
33550         if(this.thousandsDelimiter) {
33551             value += "";
33552             r = new RegExp(",", "g");
33553             value = value.replace(r, "");
33554         }
33555         
33556         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33557         return isNaN(value) ? '' : value;
33558     },
33559
33560     fixPrecision : function(value)
33561     {
33562         if(this.thousandsDelimiter) {
33563             value += "";
33564             r = new RegExp(",", "g");
33565             value = value.replace(r, "");
33566         }
33567         
33568         var nan = isNaN(value);
33569         
33570         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33571             return nan ? '' : value;
33572         }
33573         return parseFloat(value).toFixed(this.decimalPrecision);
33574     },
33575
33576     setValue : function(v)
33577     {
33578         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33579         
33580         this.value = v;
33581         
33582         if(this.rendered){
33583             
33584             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33585             
33586             this.inputEl().dom.value = (v == '') ? '' :
33587                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33588             
33589             if(!this.allowZero && v === '0') {
33590                 this.hiddenEl().dom.value = '';
33591                 this.inputEl().dom.value = '';
33592             }
33593             
33594             this.validate();
33595         }
33596     },
33597
33598     decimalPrecisionFcn : function(v)
33599     {
33600         return Math.floor(v);
33601     },
33602
33603     beforeBlur : function()
33604     {
33605         var v = this.parseValue(this.getRawValue());
33606         
33607         if(v || v === 0 || v === ''){
33608             this.setValue(v);
33609         }
33610     },
33611     
33612     hiddenEl : function()
33613     {
33614         return this.el.select('input.hidden-number-input',true).first();
33615     }
33616     
33617 });
33618
33619  
33620
33621 /*
33622 * Licence: LGPL
33623 */
33624
33625 /**
33626  * @class Roo.bootstrap.DocumentSlider
33627  * @extends Roo.bootstrap.Component
33628  * Bootstrap DocumentSlider class
33629  * 
33630  * @constructor
33631  * Create a new DocumentViewer
33632  * @param {Object} config The config object
33633  */
33634
33635 Roo.bootstrap.DocumentSlider = function(config){
33636     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33637     
33638     this.files = [];
33639     
33640     this.addEvents({
33641         /**
33642          * @event initial
33643          * Fire after initEvent
33644          * @param {Roo.bootstrap.DocumentSlider} this
33645          */
33646         "initial" : true,
33647         /**
33648          * @event update
33649          * Fire after update
33650          * @param {Roo.bootstrap.DocumentSlider} this
33651          */
33652         "update" : true,
33653         /**
33654          * @event click
33655          * Fire after click
33656          * @param {Roo.bootstrap.DocumentSlider} this
33657          */
33658         "click" : true
33659     });
33660 };
33661
33662 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33663     
33664     files : false,
33665     
33666     indicator : 0,
33667     
33668     getAutoCreate : function()
33669     {
33670         var cfg = {
33671             tag : 'div',
33672             cls : 'roo-document-slider',
33673             cn : [
33674                 {
33675                     tag : 'div',
33676                     cls : 'roo-document-slider-header',
33677                     cn : [
33678                         {
33679                             tag : 'div',
33680                             cls : 'roo-document-slider-header-title'
33681                         }
33682                     ]
33683                 },
33684                 {
33685                     tag : 'div',
33686                     cls : 'roo-document-slider-body',
33687                     cn : [
33688                         {
33689                             tag : 'div',
33690                             cls : 'roo-document-slider-prev',
33691                             cn : [
33692                                 {
33693                                     tag : 'i',
33694                                     cls : 'fa fa-chevron-left'
33695                                 }
33696                             ]
33697                         },
33698                         {
33699                             tag : 'div',
33700                             cls : 'roo-document-slider-thumb',
33701                             cn : [
33702                                 {
33703                                     tag : 'img',
33704                                     cls : 'roo-document-slider-image'
33705                                 }
33706                             ]
33707                         },
33708                         {
33709                             tag : 'div',
33710                             cls : 'roo-document-slider-next',
33711                             cn : [
33712                                 {
33713                                     tag : 'i',
33714                                     cls : 'fa fa-chevron-right'
33715                                 }
33716                             ]
33717                         }
33718                     ]
33719                 }
33720             ]
33721         };
33722         
33723         return cfg;
33724     },
33725     
33726     initEvents : function()
33727     {
33728         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33729         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33730         
33731         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33732         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33733         
33734         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33735         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33736         
33737         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33738         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33739         
33740         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33741         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33742         
33743         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33744         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33745         
33746         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33747         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33748         
33749         this.thumbEl.on('click', this.onClick, this);
33750         
33751         this.prevIndicator.on('click', this.prev, this);
33752         
33753         this.nextIndicator.on('click', this.next, this);
33754         
33755     },
33756     
33757     initial : function()
33758     {
33759         if(this.files.length){
33760             this.indicator = 1;
33761             this.update()
33762         }
33763         
33764         this.fireEvent('initial', this);
33765     },
33766     
33767     update : function()
33768     {
33769         this.imageEl.attr('src', this.files[this.indicator - 1]);
33770         
33771         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33772         
33773         this.prevIndicator.show();
33774         
33775         if(this.indicator == 1){
33776             this.prevIndicator.hide();
33777         }
33778         
33779         this.nextIndicator.show();
33780         
33781         if(this.indicator == this.files.length){
33782             this.nextIndicator.hide();
33783         }
33784         
33785         this.thumbEl.scrollTo('top');
33786         
33787         this.fireEvent('update', this);
33788     },
33789     
33790     onClick : function(e)
33791     {
33792         e.preventDefault();
33793         
33794         this.fireEvent('click', this);
33795     },
33796     
33797     prev : function(e)
33798     {
33799         e.preventDefault();
33800         
33801         this.indicator = Math.max(1, this.indicator - 1);
33802         
33803         this.update();
33804     },
33805     
33806     next : function(e)
33807     {
33808         e.preventDefault();
33809         
33810         this.indicator = Math.min(this.files.length, this.indicator + 1);
33811         
33812         this.update();
33813     }
33814 });
33815 /*
33816  * - LGPL
33817  *
33818  * RadioSet
33819  *
33820  *
33821  */
33822
33823 /**
33824  * @class Roo.bootstrap.RadioSet
33825  * @extends Roo.bootstrap.Input
33826  * Bootstrap RadioSet class
33827  * @cfg {String} indicatorpos (left|right) default left
33828  * @cfg {Boolean} inline (true|false) inline the element (default true)
33829  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33830  * @constructor
33831  * Create a new RadioSet
33832  * @param {Object} config The config object
33833  */
33834
33835 Roo.bootstrap.RadioSet = function(config){
33836     
33837     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33838     
33839     this.radioes = [];
33840     
33841     Roo.bootstrap.RadioSet.register(this);
33842     
33843     this.addEvents({
33844         /**
33845         * @event check
33846         * Fires when the element is checked or unchecked.
33847         * @param {Roo.bootstrap.RadioSet} this This radio
33848         * @param {Roo.bootstrap.Radio} item The checked item
33849         */
33850        check : true,
33851        /**
33852         * @event click
33853         * Fires when the element is click.
33854         * @param {Roo.bootstrap.RadioSet} this This radio set
33855         * @param {Roo.bootstrap.Radio} item The checked item
33856         * @param {Roo.EventObject} e The event object
33857         */
33858        click : true
33859     });
33860     
33861 };
33862
33863 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33864
33865     radioes : false,
33866     
33867     inline : true,
33868     
33869     weight : '',
33870     
33871     indicatorpos : 'left',
33872     
33873     getAutoCreate : function()
33874     {
33875         var label = {
33876             tag : 'label',
33877             cls : 'roo-radio-set-label',
33878             cn : [
33879                 {
33880                     tag : 'span',
33881                     html : this.fieldLabel
33882                 }
33883             ]
33884         };
33885         
33886         if(this.indicatorpos == 'left'){
33887             label.cn.unshift({
33888                 tag : 'i',
33889                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33890                 tooltip : 'This field is required'
33891             });
33892         } else {
33893             label.cn.push({
33894                 tag : 'i',
33895                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33896                 tooltip : 'This field is required'
33897             });
33898         }
33899         
33900         var items = {
33901             tag : 'div',
33902             cls : 'roo-radio-set-items'
33903         };
33904         
33905         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33906         
33907         if (align === 'left' && this.fieldLabel.length) {
33908             
33909             items = {
33910                 cls : "roo-radio-set-right", 
33911                 cn: [
33912                     items
33913                 ]
33914             };
33915             
33916             if(this.labelWidth > 12){
33917                 label.style = "width: " + this.labelWidth + 'px';
33918             }
33919             
33920             if(this.labelWidth < 13 && this.labelmd == 0){
33921                 this.labelmd = this.labelWidth;
33922             }
33923             
33924             if(this.labellg > 0){
33925                 label.cls += ' col-lg-' + this.labellg;
33926                 items.cls += ' col-lg-' + (12 - this.labellg);
33927             }
33928             
33929             if(this.labelmd > 0){
33930                 label.cls += ' col-md-' + this.labelmd;
33931                 items.cls += ' col-md-' + (12 - this.labelmd);
33932             }
33933             
33934             if(this.labelsm > 0){
33935                 label.cls += ' col-sm-' + this.labelsm;
33936                 items.cls += ' col-sm-' + (12 - this.labelsm);
33937             }
33938             
33939             if(this.labelxs > 0){
33940                 label.cls += ' col-xs-' + this.labelxs;
33941                 items.cls += ' col-xs-' + (12 - this.labelxs);
33942             }
33943         }
33944         
33945         var cfg = {
33946             tag : 'div',
33947             cls : 'roo-radio-set',
33948             cn : [
33949                 {
33950                     tag : 'input',
33951                     cls : 'roo-radio-set-input',
33952                     type : 'hidden',
33953                     name : this.name,
33954                     value : this.value ? this.value :  ''
33955                 },
33956                 label,
33957                 items
33958             ]
33959         };
33960         
33961         if(this.weight.length){
33962             cfg.cls += ' roo-radio-' + this.weight;
33963         }
33964         
33965         if(this.inline) {
33966             cfg.cls += ' roo-radio-set-inline';
33967         }
33968         
33969         var settings=this;
33970         ['xs','sm','md','lg'].map(function(size){
33971             if (settings[size]) {
33972                 cfg.cls += ' col-' + size + '-' + settings[size];
33973             }
33974         });
33975         
33976         return cfg;
33977         
33978     },
33979
33980     initEvents : function()
33981     {
33982         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33983         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33984         
33985         if(!this.fieldLabel.length){
33986             this.labelEl.hide();
33987         }
33988         
33989         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33990         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33991         
33992         this.indicator = this.indicatorEl();
33993         
33994         if(this.indicator){
33995             this.indicator.addClass('invisible');
33996         }
33997         
33998         this.originalValue = this.getValue();
33999         
34000     },
34001     
34002     inputEl: function ()
34003     {
34004         return this.el.select('.roo-radio-set-input', true).first();
34005     },
34006     
34007     getChildContainer : function()
34008     {
34009         return this.itemsEl;
34010     },
34011     
34012     register : function(item)
34013     {
34014         this.radioes.push(item);
34015         
34016     },
34017     
34018     validate : function()
34019     {   
34020         if(this.getVisibilityEl().hasClass('hidden')){
34021             return true;
34022         }
34023         
34024         var valid = false;
34025         
34026         Roo.each(this.radioes, function(i){
34027             if(!i.checked){
34028                 return;
34029             }
34030             
34031             valid = true;
34032             return false;
34033         });
34034         
34035         if(this.allowBlank) {
34036             return true;
34037         }
34038         
34039         if(this.disabled || valid){
34040             this.markValid();
34041             return true;
34042         }
34043         
34044         this.markInvalid();
34045         return false;
34046         
34047     },
34048     
34049     markValid : function()
34050     {
34051         if(this.labelEl.isVisible(true)){
34052             this.indicatorEl().removeClass('visible');
34053             this.indicatorEl().addClass('invisible');
34054         }
34055         
34056         this.el.removeClass([this.invalidClass, this.validClass]);
34057         this.el.addClass(this.validClass);
34058         
34059         this.fireEvent('valid', this);
34060     },
34061     
34062     markInvalid : function(msg)
34063     {
34064         if(this.allowBlank || this.disabled){
34065             return;
34066         }
34067         
34068         if(this.labelEl.isVisible(true)){
34069             this.indicatorEl().removeClass('invisible');
34070             this.indicatorEl().addClass('visible');
34071         }
34072         
34073         this.el.removeClass([this.invalidClass, this.validClass]);
34074         this.el.addClass(this.invalidClass);
34075         
34076         this.fireEvent('invalid', this, msg);
34077         
34078     },
34079     
34080     setValue : function(v, suppressEvent)
34081     {   
34082         if(this.value === v){
34083             return;
34084         }
34085         
34086         this.value = v;
34087         
34088         if(this.rendered){
34089             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34090         }
34091         
34092         Roo.each(this.radioes, function(i){
34093             i.checked = false;
34094             i.el.removeClass('checked');
34095         });
34096         
34097         Roo.each(this.radioes, function(i){
34098             
34099             if(i.value === v || i.value.toString() === v.toString()){
34100                 i.checked = true;
34101                 i.el.addClass('checked');
34102                 
34103                 if(suppressEvent !== true){
34104                     this.fireEvent('check', this, i);
34105                 }
34106                 
34107                 return false;
34108             }
34109             
34110         }, this);
34111         
34112         this.validate();
34113     },
34114     
34115     clearInvalid : function(){
34116         
34117         if(!this.el || this.preventMark){
34118             return;
34119         }
34120         
34121         this.el.removeClass([this.invalidClass]);
34122         
34123         this.fireEvent('valid', this);
34124     }
34125     
34126 });
34127
34128 Roo.apply(Roo.bootstrap.RadioSet, {
34129     
34130     groups: {},
34131     
34132     register : function(set)
34133     {
34134         this.groups[set.name] = set;
34135     },
34136     
34137     get: function(name) 
34138     {
34139         if (typeof(this.groups[name]) == 'undefined') {
34140             return false;
34141         }
34142         
34143         return this.groups[name] ;
34144     }
34145     
34146 });
34147 /*
34148  * Based on:
34149  * Ext JS Library 1.1.1
34150  * Copyright(c) 2006-2007, Ext JS, LLC.
34151  *
34152  * Originally Released Under LGPL - original licence link has changed is not relivant.
34153  *
34154  * Fork - LGPL
34155  * <script type="text/javascript">
34156  */
34157
34158
34159 /**
34160  * @class Roo.bootstrap.SplitBar
34161  * @extends Roo.util.Observable
34162  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34163  * <br><br>
34164  * Usage:
34165  * <pre><code>
34166 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34167                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34168 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34169 split.minSize = 100;
34170 split.maxSize = 600;
34171 split.animate = true;
34172 split.on('moved', splitterMoved);
34173 </code></pre>
34174  * @constructor
34175  * Create a new SplitBar
34176  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34177  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34178  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34179  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34180                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34181                         position of the SplitBar).
34182  */
34183 Roo.bootstrap.SplitBar = function(cfg){
34184     
34185     /** @private */
34186     
34187     //{
34188     //  dragElement : elm
34189     //  resizingElement: el,
34190         // optional..
34191     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34192     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34193         // existingProxy ???
34194     //}
34195     
34196     this.el = Roo.get(cfg.dragElement, true);
34197     this.el.dom.unselectable = "on";
34198     /** @private */
34199     this.resizingEl = Roo.get(cfg.resizingElement, true);
34200
34201     /**
34202      * @private
34203      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34204      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34205      * @type Number
34206      */
34207     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34208     
34209     /**
34210      * The minimum size of the resizing element. (Defaults to 0)
34211      * @type Number
34212      */
34213     this.minSize = 0;
34214     
34215     /**
34216      * The maximum size of the resizing element. (Defaults to 2000)
34217      * @type Number
34218      */
34219     this.maxSize = 2000;
34220     
34221     /**
34222      * Whether to animate the transition to the new size
34223      * @type Boolean
34224      */
34225     this.animate = false;
34226     
34227     /**
34228      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34229      * @type Boolean
34230      */
34231     this.useShim = false;
34232     
34233     /** @private */
34234     this.shim = null;
34235     
34236     if(!cfg.existingProxy){
34237         /** @private */
34238         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34239     }else{
34240         this.proxy = Roo.get(cfg.existingProxy).dom;
34241     }
34242     /** @private */
34243     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34244     
34245     /** @private */
34246     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34247     
34248     /** @private */
34249     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34250     
34251     /** @private */
34252     this.dragSpecs = {};
34253     
34254     /**
34255      * @private The adapter to use to positon and resize elements
34256      */
34257     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34258     this.adapter.init(this);
34259     
34260     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34261         /** @private */
34262         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34263         this.el.addClass("roo-splitbar-h");
34264     }else{
34265         /** @private */
34266         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34267         this.el.addClass("roo-splitbar-v");
34268     }
34269     
34270     this.addEvents({
34271         /**
34272          * @event resize
34273          * Fires when the splitter is moved (alias for {@link #event-moved})
34274          * @param {Roo.bootstrap.SplitBar} this
34275          * @param {Number} newSize the new width or height
34276          */
34277         "resize" : true,
34278         /**
34279          * @event moved
34280          * Fires when the splitter is moved
34281          * @param {Roo.bootstrap.SplitBar} this
34282          * @param {Number} newSize the new width or height
34283          */
34284         "moved" : true,
34285         /**
34286          * @event beforeresize
34287          * Fires before the splitter is dragged
34288          * @param {Roo.bootstrap.SplitBar} this
34289          */
34290         "beforeresize" : true,
34291
34292         "beforeapply" : true
34293     });
34294
34295     Roo.util.Observable.call(this);
34296 };
34297
34298 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34299     onStartProxyDrag : function(x, y){
34300         this.fireEvent("beforeresize", this);
34301         if(!this.overlay){
34302             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34303             o.unselectable();
34304             o.enableDisplayMode("block");
34305             // all splitbars share the same overlay
34306             Roo.bootstrap.SplitBar.prototype.overlay = o;
34307         }
34308         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34309         this.overlay.show();
34310         Roo.get(this.proxy).setDisplayed("block");
34311         var size = this.adapter.getElementSize(this);
34312         this.activeMinSize = this.getMinimumSize();;
34313         this.activeMaxSize = this.getMaximumSize();;
34314         var c1 = size - this.activeMinSize;
34315         var c2 = Math.max(this.activeMaxSize - size, 0);
34316         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34317             this.dd.resetConstraints();
34318             this.dd.setXConstraint(
34319                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34320                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34321             );
34322             this.dd.setYConstraint(0, 0);
34323         }else{
34324             this.dd.resetConstraints();
34325             this.dd.setXConstraint(0, 0);
34326             this.dd.setYConstraint(
34327                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34328                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34329             );
34330          }
34331         this.dragSpecs.startSize = size;
34332         this.dragSpecs.startPoint = [x, y];
34333         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34334     },
34335     
34336     /** 
34337      * @private Called after the drag operation by the DDProxy
34338      */
34339     onEndProxyDrag : function(e){
34340         Roo.get(this.proxy).setDisplayed(false);
34341         var endPoint = Roo.lib.Event.getXY(e);
34342         if(this.overlay){
34343             this.overlay.hide();
34344         }
34345         var newSize;
34346         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34347             newSize = this.dragSpecs.startSize + 
34348                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34349                     endPoint[0] - this.dragSpecs.startPoint[0] :
34350                     this.dragSpecs.startPoint[0] - endPoint[0]
34351                 );
34352         }else{
34353             newSize = this.dragSpecs.startSize + 
34354                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34355                     endPoint[1] - this.dragSpecs.startPoint[1] :
34356                     this.dragSpecs.startPoint[1] - endPoint[1]
34357                 );
34358         }
34359         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34360         if(newSize != this.dragSpecs.startSize){
34361             if(this.fireEvent('beforeapply', this, newSize) !== false){
34362                 this.adapter.setElementSize(this, newSize);
34363                 this.fireEvent("moved", this, newSize);
34364                 this.fireEvent("resize", this, newSize);
34365             }
34366         }
34367     },
34368     
34369     /**
34370      * Get the adapter this SplitBar uses
34371      * @return The adapter object
34372      */
34373     getAdapter : function(){
34374         return this.adapter;
34375     },
34376     
34377     /**
34378      * Set the adapter this SplitBar uses
34379      * @param {Object} adapter A SplitBar adapter object
34380      */
34381     setAdapter : function(adapter){
34382         this.adapter = adapter;
34383         this.adapter.init(this);
34384     },
34385     
34386     /**
34387      * Gets the minimum size for the resizing element
34388      * @return {Number} The minimum size
34389      */
34390     getMinimumSize : function(){
34391         return this.minSize;
34392     },
34393     
34394     /**
34395      * Sets the minimum size for the resizing element
34396      * @param {Number} minSize The minimum size
34397      */
34398     setMinimumSize : function(minSize){
34399         this.minSize = minSize;
34400     },
34401     
34402     /**
34403      * Gets the maximum size for the resizing element
34404      * @return {Number} The maximum size
34405      */
34406     getMaximumSize : function(){
34407         return this.maxSize;
34408     },
34409     
34410     /**
34411      * Sets the maximum size for the resizing element
34412      * @param {Number} maxSize The maximum size
34413      */
34414     setMaximumSize : function(maxSize){
34415         this.maxSize = maxSize;
34416     },
34417     
34418     /**
34419      * Sets the initialize size for the resizing element
34420      * @param {Number} size The initial size
34421      */
34422     setCurrentSize : function(size){
34423         var oldAnimate = this.animate;
34424         this.animate = false;
34425         this.adapter.setElementSize(this, size);
34426         this.animate = oldAnimate;
34427     },
34428     
34429     /**
34430      * Destroy this splitbar. 
34431      * @param {Boolean} removeEl True to remove the element
34432      */
34433     destroy : function(removeEl){
34434         if(this.shim){
34435             this.shim.remove();
34436         }
34437         this.dd.unreg();
34438         this.proxy.parentNode.removeChild(this.proxy);
34439         if(removeEl){
34440             this.el.remove();
34441         }
34442     }
34443 });
34444
34445 /**
34446  * @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.
34447  */
34448 Roo.bootstrap.SplitBar.createProxy = function(dir){
34449     var proxy = new Roo.Element(document.createElement("div"));
34450     proxy.unselectable();
34451     var cls = 'roo-splitbar-proxy';
34452     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34453     document.body.appendChild(proxy.dom);
34454     return proxy.dom;
34455 };
34456
34457 /** 
34458  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34459  * Default Adapter. It assumes the splitter and resizing element are not positioned
34460  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34461  */
34462 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34463 };
34464
34465 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34466     // do nothing for now
34467     init : function(s){
34468     
34469     },
34470     /**
34471      * Called before drag operations to get the current size of the resizing element. 
34472      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34473      */
34474      getElementSize : function(s){
34475         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34476             return s.resizingEl.getWidth();
34477         }else{
34478             return s.resizingEl.getHeight();
34479         }
34480     },
34481     
34482     /**
34483      * Called after drag operations to set the size of the resizing element.
34484      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34485      * @param {Number} newSize The new size to set
34486      * @param {Function} onComplete A function to be invoked when resizing is complete
34487      */
34488     setElementSize : function(s, newSize, onComplete){
34489         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34490             if(!s.animate){
34491                 s.resizingEl.setWidth(newSize);
34492                 if(onComplete){
34493                     onComplete(s, newSize);
34494                 }
34495             }else{
34496                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34497             }
34498         }else{
34499             
34500             if(!s.animate){
34501                 s.resizingEl.setHeight(newSize);
34502                 if(onComplete){
34503                     onComplete(s, newSize);
34504                 }
34505             }else{
34506                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34507             }
34508         }
34509     }
34510 };
34511
34512 /** 
34513  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34514  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34515  * Adapter that  moves the splitter element to align with the resized sizing element. 
34516  * Used with an absolute positioned SplitBar.
34517  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34518  * document.body, make sure you assign an id to the body element.
34519  */
34520 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34521     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34522     this.container = Roo.get(container);
34523 };
34524
34525 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34526     init : function(s){
34527         this.basic.init(s);
34528     },
34529     
34530     getElementSize : function(s){
34531         return this.basic.getElementSize(s);
34532     },
34533     
34534     setElementSize : function(s, newSize, onComplete){
34535         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34536     },
34537     
34538     moveSplitter : function(s){
34539         var yes = Roo.bootstrap.SplitBar;
34540         switch(s.placement){
34541             case yes.LEFT:
34542                 s.el.setX(s.resizingEl.getRight());
34543                 break;
34544             case yes.RIGHT:
34545                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34546                 break;
34547             case yes.TOP:
34548                 s.el.setY(s.resizingEl.getBottom());
34549                 break;
34550             case yes.BOTTOM:
34551                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34552                 break;
34553         }
34554     }
34555 };
34556
34557 /**
34558  * Orientation constant - Create a vertical SplitBar
34559  * @static
34560  * @type Number
34561  */
34562 Roo.bootstrap.SplitBar.VERTICAL = 1;
34563
34564 /**
34565  * Orientation constant - Create a horizontal SplitBar
34566  * @static
34567  * @type Number
34568  */
34569 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34570
34571 /**
34572  * Placement constant - The resizing element is to the left of the splitter element
34573  * @static
34574  * @type Number
34575  */
34576 Roo.bootstrap.SplitBar.LEFT = 1;
34577
34578 /**
34579  * Placement constant - The resizing element is to the right of the splitter element
34580  * @static
34581  * @type Number
34582  */
34583 Roo.bootstrap.SplitBar.RIGHT = 2;
34584
34585 /**
34586  * Placement constant - The resizing element is positioned above the splitter element
34587  * @static
34588  * @type Number
34589  */
34590 Roo.bootstrap.SplitBar.TOP = 3;
34591
34592 /**
34593  * Placement constant - The resizing element is positioned under splitter element
34594  * @static
34595  * @type Number
34596  */
34597 Roo.bootstrap.SplitBar.BOTTOM = 4;
34598 Roo.namespace("Roo.bootstrap.layout");/*
34599  * Based on:
34600  * Ext JS Library 1.1.1
34601  * Copyright(c) 2006-2007, Ext JS, LLC.
34602  *
34603  * Originally Released Under LGPL - original licence link has changed is not relivant.
34604  *
34605  * Fork - LGPL
34606  * <script type="text/javascript">
34607  */
34608
34609 /**
34610  * @class Roo.bootstrap.layout.Manager
34611  * @extends Roo.bootstrap.Component
34612  * Base class for layout managers.
34613  */
34614 Roo.bootstrap.layout.Manager = function(config)
34615 {
34616     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34617
34618
34619
34620
34621
34622     /** false to disable window resize monitoring @type Boolean */
34623     this.monitorWindowResize = true;
34624     this.regions = {};
34625     this.addEvents({
34626         /**
34627          * @event layout
34628          * Fires when a layout is performed.
34629          * @param {Roo.LayoutManager} this
34630          */
34631         "layout" : true,
34632         /**
34633          * @event regionresized
34634          * Fires when the user resizes a region.
34635          * @param {Roo.LayoutRegion} region The resized region
34636          * @param {Number} newSize The new size (width for east/west, height for north/south)
34637          */
34638         "regionresized" : true,
34639         /**
34640          * @event regioncollapsed
34641          * Fires when a region is collapsed.
34642          * @param {Roo.LayoutRegion} region The collapsed region
34643          */
34644         "regioncollapsed" : true,
34645         /**
34646          * @event regionexpanded
34647          * Fires when a region is expanded.
34648          * @param {Roo.LayoutRegion} region The expanded region
34649          */
34650         "regionexpanded" : true
34651     });
34652     this.updating = false;
34653
34654     if (config.el) {
34655         this.el = Roo.get(config.el);
34656         this.initEvents();
34657     }
34658
34659 };
34660
34661 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34662
34663
34664     regions : null,
34665
34666     monitorWindowResize : true,
34667
34668
34669     updating : false,
34670
34671
34672     onRender : function(ct, position)
34673     {
34674         if(!this.el){
34675             this.el = Roo.get(ct);
34676             this.initEvents();
34677         }
34678         //this.fireEvent('render',this);
34679     },
34680
34681
34682     initEvents: function()
34683     {
34684
34685
34686         // ie scrollbar fix
34687         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34688             document.body.scroll = "no";
34689         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34690             this.el.position('relative');
34691         }
34692         this.id = this.el.id;
34693         this.el.addClass("roo-layout-container");
34694         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34695         if(this.el.dom != document.body ) {
34696             this.el.on('resize', this.layout,this);
34697             this.el.on('show', this.layout,this);
34698         }
34699
34700     },
34701
34702     /**
34703      * Returns true if this layout is currently being updated
34704      * @return {Boolean}
34705      */
34706     isUpdating : function(){
34707         return this.updating;
34708     },
34709
34710     /**
34711      * Suspend the LayoutManager from doing auto-layouts while
34712      * making multiple add or remove calls
34713      */
34714     beginUpdate : function(){
34715         this.updating = true;
34716     },
34717
34718     /**
34719      * Restore auto-layouts and optionally disable the manager from performing a layout
34720      * @param {Boolean} noLayout true to disable a layout update
34721      */
34722     endUpdate : function(noLayout){
34723         this.updating = false;
34724         if(!noLayout){
34725             this.layout();
34726         }
34727     },
34728
34729     layout: function(){
34730         // abstract...
34731     },
34732
34733     onRegionResized : function(region, newSize){
34734         this.fireEvent("regionresized", region, newSize);
34735         this.layout();
34736     },
34737
34738     onRegionCollapsed : function(region){
34739         this.fireEvent("regioncollapsed", region);
34740     },
34741
34742     onRegionExpanded : function(region){
34743         this.fireEvent("regionexpanded", region);
34744     },
34745
34746     /**
34747      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34748      * performs box-model adjustments.
34749      * @return {Object} The size as an object {width: (the width), height: (the height)}
34750      */
34751     getViewSize : function()
34752     {
34753         var size;
34754         if(this.el.dom != document.body){
34755             size = this.el.getSize();
34756         }else{
34757             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34758         }
34759         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34760         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34761         return size;
34762     },
34763
34764     /**
34765      * Returns the Element this layout is bound to.
34766      * @return {Roo.Element}
34767      */
34768     getEl : function(){
34769         return this.el;
34770     },
34771
34772     /**
34773      * Returns the specified region.
34774      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34775      * @return {Roo.LayoutRegion}
34776      */
34777     getRegion : function(target){
34778         return this.regions[target.toLowerCase()];
34779     },
34780
34781     onWindowResize : function(){
34782         if(this.monitorWindowResize){
34783             this.layout();
34784         }
34785     }
34786 });
34787 /*
34788  * Based on:
34789  * Ext JS Library 1.1.1
34790  * Copyright(c) 2006-2007, Ext JS, LLC.
34791  *
34792  * Originally Released Under LGPL - original licence link has changed is not relivant.
34793  *
34794  * Fork - LGPL
34795  * <script type="text/javascript">
34796  */
34797 /**
34798  * @class Roo.bootstrap.layout.Border
34799  * @extends Roo.bootstrap.layout.Manager
34800  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34801  * please see: examples/bootstrap/nested.html<br><br>
34802  
34803 <b>The container the layout is rendered into can be either the body element or any other element.
34804 If it is not the body element, the container needs to either be an absolute positioned element,
34805 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34806 the container size if it is not the body element.</b>
34807
34808 * @constructor
34809 * Create a new Border
34810 * @param {Object} config Configuration options
34811  */
34812 Roo.bootstrap.layout.Border = function(config){
34813     config = config || {};
34814     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34815     
34816     
34817     
34818     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34819         if(config[region]){
34820             config[region].region = region;
34821             this.addRegion(config[region]);
34822         }
34823     },this);
34824     
34825 };
34826
34827 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34828
34829 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34830     /**
34831      * Creates and adds a new region if it doesn't already exist.
34832      * @param {String} target The target region key (north, south, east, west or center).
34833      * @param {Object} config The regions config object
34834      * @return {BorderLayoutRegion} The new region
34835      */
34836     addRegion : function(config)
34837     {
34838         if(!this.regions[config.region]){
34839             var r = this.factory(config);
34840             this.bindRegion(r);
34841         }
34842         return this.regions[config.region];
34843     },
34844
34845     // private (kinda)
34846     bindRegion : function(r){
34847         this.regions[r.config.region] = r;
34848         
34849         r.on("visibilitychange",    this.layout, this);
34850         r.on("paneladded",          this.layout, this);
34851         r.on("panelremoved",        this.layout, this);
34852         r.on("invalidated",         this.layout, this);
34853         r.on("resized",             this.onRegionResized, this);
34854         r.on("collapsed",           this.onRegionCollapsed, this);
34855         r.on("expanded",            this.onRegionExpanded, this);
34856     },
34857
34858     /**
34859      * Performs a layout update.
34860      */
34861     layout : function()
34862     {
34863         if(this.updating) {
34864             return;
34865         }
34866         
34867         // render all the rebions if they have not been done alreayd?
34868         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34869             if(this.regions[region] && !this.regions[region].bodyEl){
34870                 this.regions[region].onRender(this.el)
34871             }
34872         },this);
34873         
34874         var size = this.getViewSize();
34875         var w = size.width;
34876         var h = size.height;
34877         var centerW = w;
34878         var centerH = h;
34879         var centerY = 0;
34880         var centerX = 0;
34881         //var x = 0, y = 0;
34882
34883         var rs = this.regions;
34884         var north = rs["north"];
34885         var south = rs["south"]; 
34886         var west = rs["west"];
34887         var east = rs["east"];
34888         var center = rs["center"];
34889         //if(this.hideOnLayout){ // not supported anymore
34890             //c.el.setStyle("display", "none");
34891         //}
34892         if(north && north.isVisible()){
34893             var b = north.getBox();
34894             var m = north.getMargins();
34895             b.width = w - (m.left+m.right);
34896             b.x = m.left;
34897             b.y = m.top;
34898             centerY = b.height + b.y + m.bottom;
34899             centerH -= centerY;
34900             north.updateBox(this.safeBox(b));
34901         }
34902         if(south && south.isVisible()){
34903             var b = south.getBox();
34904             var m = south.getMargins();
34905             b.width = w - (m.left+m.right);
34906             b.x = m.left;
34907             var totalHeight = (b.height + m.top + m.bottom);
34908             b.y = h - totalHeight + m.top;
34909             centerH -= totalHeight;
34910             south.updateBox(this.safeBox(b));
34911         }
34912         if(west && west.isVisible()){
34913             var b = west.getBox();
34914             var m = west.getMargins();
34915             b.height = centerH - (m.top+m.bottom);
34916             b.x = m.left;
34917             b.y = centerY + m.top;
34918             var totalWidth = (b.width + m.left + m.right);
34919             centerX += totalWidth;
34920             centerW -= totalWidth;
34921             west.updateBox(this.safeBox(b));
34922         }
34923         if(east && east.isVisible()){
34924             var b = east.getBox();
34925             var m = east.getMargins();
34926             b.height = centerH - (m.top+m.bottom);
34927             var totalWidth = (b.width + m.left + m.right);
34928             b.x = w - totalWidth + m.left;
34929             b.y = centerY + m.top;
34930             centerW -= totalWidth;
34931             east.updateBox(this.safeBox(b));
34932         }
34933         if(center){
34934             var m = center.getMargins();
34935             var centerBox = {
34936                 x: centerX + m.left,
34937                 y: centerY + m.top,
34938                 width: centerW - (m.left+m.right),
34939                 height: centerH - (m.top+m.bottom)
34940             };
34941             //if(this.hideOnLayout){
34942                 //center.el.setStyle("display", "block");
34943             //}
34944             center.updateBox(this.safeBox(centerBox));
34945         }
34946         this.el.repaint();
34947         this.fireEvent("layout", this);
34948     },
34949
34950     // private
34951     safeBox : function(box){
34952         box.width = Math.max(0, box.width);
34953         box.height = Math.max(0, box.height);
34954         return box;
34955     },
34956
34957     /**
34958      * Adds a ContentPanel (or subclass) to this layout.
34959      * @param {String} target The target region key (north, south, east, west or center).
34960      * @param {Roo.ContentPanel} panel The panel to add
34961      * @return {Roo.ContentPanel} The added panel
34962      */
34963     add : function(target, panel){
34964          
34965         target = target.toLowerCase();
34966         return this.regions[target].add(panel);
34967     },
34968
34969     /**
34970      * Remove a ContentPanel (or subclass) to this layout.
34971      * @param {String} target The target region key (north, south, east, west or center).
34972      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34973      * @return {Roo.ContentPanel} The removed panel
34974      */
34975     remove : function(target, panel){
34976         target = target.toLowerCase();
34977         return this.regions[target].remove(panel);
34978     },
34979
34980     /**
34981      * Searches all regions for a panel with the specified id
34982      * @param {String} panelId
34983      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34984      */
34985     findPanel : function(panelId){
34986         var rs = this.regions;
34987         for(var target in rs){
34988             if(typeof rs[target] != "function"){
34989                 var p = rs[target].getPanel(panelId);
34990                 if(p){
34991                     return p;
34992                 }
34993             }
34994         }
34995         return null;
34996     },
34997
34998     /**
34999      * Searches all regions for a panel with the specified id and activates (shows) it.
35000      * @param {String/ContentPanel} panelId The panels id or the panel itself
35001      * @return {Roo.ContentPanel} The shown panel or null
35002      */
35003     showPanel : function(panelId) {
35004       var rs = this.regions;
35005       for(var target in rs){
35006          var r = rs[target];
35007          if(typeof r != "function"){
35008             if(r.hasPanel(panelId)){
35009                return r.showPanel(panelId);
35010             }
35011          }
35012       }
35013       return null;
35014    },
35015
35016    /**
35017      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35018      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35019      */
35020    /*
35021     restoreState : function(provider){
35022         if(!provider){
35023             provider = Roo.state.Manager;
35024         }
35025         var sm = new Roo.LayoutStateManager();
35026         sm.init(this, provider);
35027     },
35028 */
35029  
35030  
35031     /**
35032      * Adds a xtype elements to the layout.
35033      * <pre><code>
35034
35035 layout.addxtype({
35036        xtype : 'ContentPanel',
35037        region: 'west',
35038        items: [ .... ]
35039    }
35040 );
35041
35042 layout.addxtype({
35043         xtype : 'NestedLayoutPanel',
35044         region: 'west',
35045         layout: {
35046            center: { },
35047            west: { }   
35048         },
35049         items : [ ... list of content panels or nested layout panels.. ]
35050    }
35051 );
35052 </code></pre>
35053      * @param {Object} cfg Xtype definition of item to add.
35054      */
35055     addxtype : function(cfg)
35056     {
35057         // basically accepts a pannel...
35058         // can accept a layout region..!?!?
35059         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35060         
35061         
35062         // theory?  children can only be panels??
35063         
35064         //if (!cfg.xtype.match(/Panel$/)) {
35065         //    return false;
35066         //}
35067         var ret = false;
35068         
35069         if (typeof(cfg.region) == 'undefined') {
35070             Roo.log("Failed to add Panel, region was not set");
35071             Roo.log(cfg);
35072             return false;
35073         }
35074         var region = cfg.region;
35075         delete cfg.region;
35076         
35077           
35078         var xitems = [];
35079         if (cfg.items) {
35080             xitems = cfg.items;
35081             delete cfg.items;
35082         }
35083         var nb = false;
35084         
35085         switch(cfg.xtype) 
35086         {
35087             case 'Content':  // ContentPanel (el, cfg)
35088             case 'Scroll':  // ContentPanel (el, cfg)
35089             case 'View': 
35090                 cfg.autoCreate = true;
35091                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35092                 //} else {
35093                 //    var el = this.el.createChild();
35094                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35095                 //}
35096                 
35097                 this.add(region, ret);
35098                 break;
35099             
35100             /*
35101             case 'TreePanel': // our new panel!
35102                 cfg.el = this.el.createChild();
35103                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35104                 this.add(region, ret);
35105                 break;
35106             */
35107             
35108             case 'Nest': 
35109                 // create a new Layout (which is  a Border Layout...
35110                 
35111                 var clayout = cfg.layout;
35112                 clayout.el  = this.el.createChild();
35113                 clayout.items   = clayout.items  || [];
35114                 
35115                 delete cfg.layout;
35116                 
35117                 // replace this exitems with the clayout ones..
35118                 xitems = clayout.items;
35119                  
35120                 // force background off if it's in center...
35121                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35122                     cfg.background = false;
35123                 }
35124                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35125                 
35126                 
35127                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35128                 //console.log('adding nested layout panel '  + cfg.toSource());
35129                 this.add(region, ret);
35130                 nb = {}; /// find first...
35131                 break;
35132             
35133             case 'Grid':
35134                 
35135                 // needs grid and region
35136                 
35137                 //var el = this.getRegion(region).el.createChild();
35138                 /*
35139                  *var el = this.el.createChild();
35140                 // create the grid first...
35141                 cfg.grid.container = el;
35142                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35143                 */
35144                 
35145                 if (region == 'center' && this.active ) {
35146                     cfg.background = false;
35147                 }
35148                 
35149                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35150                 
35151                 this.add(region, ret);
35152                 /*
35153                 if (cfg.background) {
35154                     // render grid on panel activation (if panel background)
35155                     ret.on('activate', function(gp) {
35156                         if (!gp.grid.rendered) {
35157                     //        gp.grid.render(el);
35158                         }
35159                     });
35160                 } else {
35161                   //  cfg.grid.render(el);
35162                 }
35163                 */
35164                 break;
35165            
35166            
35167             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35168                 // it was the old xcomponent building that caused this before.
35169                 // espeically if border is the top element in the tree.
35170                 ret = this;
35171                 break; 
35172                 
35173                     
35174                 
35175                 
35176                 
35177             default:
35178                 /*
35179                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35180                     
35181                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35182                     this.add(region, ret);
35183                 } else {
35184                 */
35185                     Roo.log(cfg);
35186                     throw "Can not add '" + cfg.xtype + "' to Border";
35187                     return null;
35188              
35189                                 
35190              
35191         }
35192         this.beginUpdate();
35193         // add children..
35194         var region = '';
35195         var abn = {};
35196         Roo.each(xitems, function(i)  {
35197             region = nb && i.region ? i.region : false;
35198             
35199             var add = ret.addxtype(i);
35200            
35201             if (region) {
35202                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35203                 if (!i.background) {
35204                     abn[region] = nb[region] ;
35205                 }
35206             }
35207             
35208         });
35209         this.endUpdate();
35210
35211         // make the last non-background panel active..
35212         //if (nb) { Roo.log(abn); }
35213         if (nb) {
35214             
35215             for(var r in abn) {
35216                 region = this.getRegion(r);
35217                 if (region) {
35218                     // tried using nb[r], but it does not work..
35219                      
35220                     region.showPanel(abn[r]);
35221                    
35222                 }
35223             }
35224         }
35225         return ret;
35226         
35227     },
35228     
35229     
35230 // private
35231     factory : function(cfg)
35232     {
35233         
35234         var validRegions = Roo.bootstrap.layout.Border.regions;
35235
35236         var target = cfg.region;
35237         cfg.mgr = this;
35238         
35239         var r = Roo.bootstrap.layout;
35240         Roo.log(target);
35241         switch(target){
35242             case "north":
35243                 return new r.North(cfg);
35244             case "south":
35245                 return new r.South(cfg);
35246             case "east":
35247                 return new r.East(cfg);
35248             case "west":
35249                 return new r.West(cfg);
35250             case "center":
35251                 return new r.Center(cfg);
35252         }
35253         throw 'Layout region "'+target+'" not supported.';
35254     }
35255     
35256     
35257 });
35258  /*
35259  * Based on:
35260  * Ext JS Library 1.1.1
35261  * Copyright(c) 2006-2007, Ext JS, LLC.
35262  *
35263  * Originally Released Under LGPL - original licence link has changed is not relivant.
35264  *
35265  * Fork - LGPL
35266  * <script type="text/javascript">
35267  */
35268  
35269 /**
35270  * @class Roo.bootstrap.layout.Basic
35271  * @extends Roo.util.Observable
35272  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35273  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35274  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35275  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35276  * @cfg {string}   region  the region that it inhabits..
35277  * @cfg {bool}   skipConfig skip config?
35278  * 
35279
35280  */
35281 Roo.bootstrap.layout.Basic = function(config){
35282     
35283     this.mgr = config.mgr;
35284     
35285     this.position = config.region;
35286     
35287     var skipConfig = config.skipConfig;
35288     
35289     this.events = {
35290         /**
35291          * @scope Roo.BasicLayoutRegion
35292          */
35293         
35294         /**
35295          * @event beforeremove
35296          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35297          * @param {Roo.LayoutRegion} this
35298          * @param {Roo.ContentPanel} panel The panel
35299          * @param {Object} e The cancel event object
35300          */
35301         "beforeremove" : true,
35302         /**
35303          * @event invalidated
35304          * Fires when the layout for this region is changed.
35305          * @param {Roo.LayoutRegion} this
35306          */
35307         "invalidated" : true,
35308         /**
35309          * @event visibilitychange
35310          * Fires when this region is shown or hidden 
35311          * @param {Roo.LayoutRegion} this
35312          * @param {Boolean} visibility true or false
35313          */
35314         "visibilitychange" : true,
35315         /**
35316          * @event paneladded
35317          * Fires when a panel is added. 
35318          * @param {Roo.LayoutRegion} this
35319          * @param {Roo.ContentPanel} panel The panel
35320          */
35321         "paneladded" : true,
35322         /**
35323          * @event panelremoved
35324          * Fires when a panel is removed. 
35325          * @param {Roo.LayoutRegion} this
35326          * @param {Roo.ContentPanel} panel The panel
35327          */
35328         "panelremoved" : true,
35329         /**
35330          * @event beforecollapse
35331          * Fires when this region before collapse.
35332          * @param {Roo.LayoutRegion} this
35333          */
35334         "beforecollapse" : true,
35335         /**
35336          * @event collapsed
35337          * Fires when this region is collapsed.
35338          * @param {Roo.LayoutRegion} this
35339          */
35340         "collapsed" : true,
35341         /**
35342          * @event expanded
35343          * Fires when this region is expanded.
35344          * @param {Roo.LayoutRegion} this
35345          */
35346         "expanded" : true,
35347         /**
35348          * @event slideshow
35349          * Fires when this region is slid into view.
35350          * @param {Roo.LayoutRegion} this
35351          */
35352         "slideshow" : true,
35353         /**
35354          * @event slidehide
35355          * Fires when this region slides out of view. 
35356          * @param {Roo.LayoutRegion} this
35357          */
35358         "slidehide" : true,
35359         /**
35360          * @event panelactivated
35361          * Fires when a panel is activated. 
35362          * @param {Roo.LayoutRegion} this
35363          * @param {Roo.ContentPanel} panel The activated panel
35364          */
35365         "panelactivated" : true,
35366         /**
35367          * @event resized
35368          * Fires when the user resizes this region. 
35369          * @param {Roo.LayoutRegion} this
35370          * @param {Number} newSize The new size (width for east/west, height for north/south)
35371          */
35372         "resized" : true
35373     };
35374     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35375     this.panels = new Roo.util.MixedCollection();
35376     this.panels.getKey = this.getPanelId.createDelegate(this);
35377     this.box = null;
35378     this.activePanel = null;
35379     // ensure listeners are added...
35380     
35381     if (config.listeners || config.events) {
35382         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35383             listeners : config.listeners || {},
35384             events : config.events || {}
35385         });
35386     }
35387     
35388     if(skipConfig !== true){
35389         this.applyConfig(config);
35390     }
35391 };
35392
35393 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35394 {
35395     getPanelId : function(p){
35396         return p.getId();
35397     },
35398     
35399     applyConfig : function(config){
35400         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35401         this.config = config;
35402         
35403     },
35404     
35405     /**
35406      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35407      * the width, for horizontal (north, south) the height.
35408      * @param {Number} newSize The new width or height
35409      */
35410     resizeTo : function(newSize){
35411         var el = this.el ? this.el :
35412                  (this.activePanel ? this.activePanel.getEl() : null);
35413         if(el){
35414             switch(this.position){
35415                 case "east":
35416                 case "west":
35417                     el.setWidth(newSize);
35418                     this.fireEvent("resized", this, newSize);
35419                 break;
35420                 case "north":
35421                 case "south":
35422                     el.setHeight(newSize);
35423                     this.fireEvent("resized", this, newSize);
35424                 break;                
35425             }
35426         }
35427     },
35428     
35429     getBox : function(){
35430         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35431     },
35432     
35433     getMargins : function(){
35434         return this.margins;
35435     },
35436     
35437     updateBox : function(box){
35438         this.box = box;
35439         var el = this.activePanel.getEl();
35440         el.dom.style.left = box.x + "px";
35441         el.dom.style.top = box.y + "px";
35442         this.activePanel.setSize(box.width, box.height);
35443     },
35444     
35445     /**
35446      * Returns the container element for this region.
35447      * @return {Roo.Element}
35448      */
35449     getEl : function(){
35450         return this.activePanel;
35451     },
35452     
35453     /**
35454      * Returns true if this region is currently visible.
35455      * @return {Boolean}
35456      */
35457     isVisible : function(){
35458         return this.activePanel ? true : false;
35459     },
35460     
35461     setActivePanel : function(panel){
35462         panel = this.getPanel(panel);
35463         if(this.activePanel && this.activePanel != panel){
35464             this.activePanel.setActiveState(false);
35465             this.activePanel.getEl().setLeftTop(-10000,-10000);
35466         }
35467         this.activePanel = panel;
35468         panel.setActiveState(true);
35469         if(this.box){
35470             panel.setSize(this.box.width, this.box.height);
35471         }
35472         this.fireEvent("panelactivated", this, panel);
35473         this.fireEvent("invalidated");
35474     },
35475     
35476     /**
35477      * Show the specified panel.
35478      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35479      * @return {Roo.ContentPanel} The shown panel or null
35480      */
35481     showPanel : function(panel){
35482         panel = this.getPanel(panel);
35483         if(panel){
35484             this.setActivePanel(panel);
35485         }
35486         return panel;
35487     },
35488     
35489     /**
35490      * Get the active panel for this region.
35491      * @return {Roo.ContentPanel} The active panel or null
35492      */
35493     getActivePanel : function(){
35494         return this.activePanel;
35495     },
35496     
35497     /**
35498      * Add the passed ContentPanel(s)
35499      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35500      * @return {Roo.ContentPanel} The panel added (if only one was added)
35501      */
35502     add : function(panel){
35503         if(arguments.length > 1){
35504             for(var i = 0, len = arguments.length; i < len; i++) {
35505                 this.add(arguments[i]);
35506             }
35507             return null;
35508         }
35509         if(this.hasPanel(panel)){
35510             this.showPanel(panel);
35511             return panel;
35512         }
35513         var el = panel.getEl();
35514         if(el.dom.parentNode != this.mgr.el.dom){
35515             this.mgr.el.dom.appendChild(el.dom);
35516         }
35517         if(panel.setRegion){
35518             panel.setRegion(this);
35519         }
35520         this.panels.add(panel);
35521         el.setStyle("position", "absolute");
35522         if(!panel.background){
35523             this.setActivePanel(panel);
35524             if(this.config.initialSize && this.panels.getCount()==1){
35525                 this.resizeTo(this.config.initialSize);
35526             }
35527         }
35528         this.fireEvent("paneladded", this, panel);
35529         return panel;
35530     },
35531     
35532     /**
35533      * Returns true if the panel is in this region.
35534      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35535      * @return {Boolean}
35536      */
35537     hasPanel : function(panel){
35538         if(typeof panel == "object"){ // must be panel obj
35539             panel = panel.getId();
35540         }
35541         return this.getPanel(panel) ? true : false;
35542     },
35543     
35544     /**
35545      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35546      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35547      * @param {Boolean} preservePanel Overrides the config preservePanel option
35548      * @return {Roo.ContentPanel} The panel that was removed
35549      */
35550     remove : function(panel, preservePanel){
35551         panel = this.getPanel(panel);
35552         if(!panel){
35553             return null;
35554         }
35555         var e = {};
35556         this.fireEvent("beforeremove", this, panel, e);
35557         if(e.cancel === true){
35558             return null;
35559         }
35560         var panelId = panel.getId();
35561         this.panels.removeKey(panelId);
35562         return panel;
35563     },
35564     
35565     /**
35566      * Returns the panel specified or null if it's not in this region.
35567      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35568      * @return {Roo.ContentPanel}
35569      */
35570     getPanel : function(id){
35571         if(typeof id == "object"){ // must be panel obj
35572             return id;
35573         }
35574         return this.panels.get(id);
35575     },
35576     
35577     /**
35578      * Returns this regions position (north/south/east/west/center).
35579      * @return {String} 
35580      */
35581     getPosition: function(){
35582         return this.position;    
35583     }
35584 });/*
35585  * Based on:
35586  * Ext JS Library 1.1.1
35587  * Copyright(c) 2006-2007, Ext JS, LLC.
35588  *
35589  * Originally Released Under LGPL - original licence link has changed is not relivant.
35590  *
35591  * Fork - LGPL
35592  * <script type="text/javascript">
35593  */
35594  
35595 /**
35596  * @class Roo.bootstrap.layout.Region
35597  * @extends Roo.bootstrap.layout.Basic
35598  * This class represents a region in a layout manager.
35599  
35600  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35601  * @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})
35602  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35603  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35604  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35605  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35606  * @cfg {String}    title           The title for the region (overrides panel titles)
35607  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35608  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35609  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35610  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35611  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35612  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35613  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35614  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35615  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35616  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35617
35618  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35619  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35620  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35621  * @cfg {Number}    width           For East/West panels
35622  * @cfg {Number}    height          For North/South panels
35623  * @cfg {Boolean}   split           To show the splitter
35624  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35625  * 
35626  * @cfg {string}   cls             Extra CSS classes to add to region
35627  * 
35628  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35629  * @cfg {string}   region  the region that it inhabits..
35630  *
35631
35632  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35633  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35634
35635  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35636  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35637  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35638  */
35639 Roo.bootstrap.layout.Region = function(config)
35640 {
35641     this.applyConfig(config);
35642
35643     var mgr = config.mgr;
35644     var pos = config.region;
35645     config.skipConfig = true;
35646     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35647     
35648     if (mgr.el) {
35649         this.onRender(mgr.el);   
35650     }
35651      
35652     this.visible = true;
35653     this.collapsed = false;
35654     this.unrendered_panels = [];
35655 };
35656
35657 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35658
35659     position: '', // set by wrapper (eg. north/south etc..)
35660     unrendered_panels : null,  // unrendered panels.
35661     createBody : function(){
35662         /** This region's body element 
35663         * @type Roo.Element */
35664         this.bodyEl = this.el.createChild({
35665                 tag: "div",
35666                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35667         });
35668     },
35669
35670     onRender: function(ctr, pos)
35671     {
35672         var dh = Roo.DomHelper;
35673         /** This region's container element 
35674         * @type Roo.Element */
35675         this.el = dh.append(ctr.dom, {
35676                 tag: "div",
35677                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35678             }, true);
35679         /** This region's title element 
35680         * @type Roo.Element */
35681     
35682         this.titleEl = dh.append(this.el.dom,
35683             {
35684                     tag: "div",
35685                     unselectable: "on",
35686                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35687                     children:[
35688                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35689                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35690                     ]}, true);
35691         
35692         this.titleEl.enableDisplayMode();
35693         /** This region's title text element 
35694         * @type HTMLElement */
35695         this.titleTextEl = this.titleEl.dom.firstChild;
35696         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35697         /*
35698         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35699         this.closeBtn.enableDisplayMode();
35700         this.closeBtn.on("click", this.closeClicked, this);
35701         this.closeBtn.hide();
35702     */
35703         this.createBody(this.config);
35704         if(this.config.hideWhenEmpty){
35705             this.hide();
35706             this.on("paneladded", this.validateVisibility, this);
35707             this.on("panelremoved", this.validateVisibility, this);
35708         }
35709         if(this.autoScroll){
35710             this.bodyEl.setStyle("overflow", "auto");
35711         }else{
35712             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35713         }
35714         //if(c.titlebar !== false){
35715             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35716                 this.titleEl.hide();
35717             }else{
35718                 this.titleEl.show();
35719                 if(this.config.title){
35720                     this.titleTextEl.innerHTML = this.config.title;
35721                 }
35722             }
35723         //}
35724         if(this.config.collapsed){
35725             this.collapse(true);
35726         }
35727         if(this.config.hidden){
35728             this.hide();
35729         }
35730         
35731         if (this.unrendered_panels && this.unrendered_panels.length) {
35732             for (var i =0;i< this.unrendered_panels.length; i++) {
35733                 this.add(this.unrendered_panels[i]);
35734             }
35735             this.unrendered_panels = null;
35736             
35737         }
35738         
35739     },
35740     
35741     applyConfig : function(c)
35742     {
35743         /*
35744          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35745             var dh = Roo.DomHelper;
35746             if(c.titlebar !== false){
35747                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35748                 this.collapseBtn.on("click", this.collapse, this);
35749                 this.collapseBtn.enableDisplayMode();
35750                 /*
35751                 if(c.showPin === true || this.showPin){
35752                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35753                     this.stickBtn.enableDisplayMode();
35754                     this.stickBtn.on("click", this.expand, this);
35755                     this.stickBtn.hide();
35756                 }
35757                 
35758             }
35759             */
35760             /** This region's collapsed element
35761             * @type Roo.Element */
35762             /*
35763              *
35764             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35765                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35766             ]}, true);
35767             
35768             if(c.floatable !== false){
35769                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35770                this.collapsedEl.on("click", this.collapseClick, this);
35771             }
35772
35773             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35774                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35775                    id: "message", unselectable: "on", style:{"float":"left"}});
35776                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35777              }
35778             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35779             this.expandBtn.on("click", this.expand, this);
35780             
35781         }
35782         
35783         if(this.collapseBtn){
35784             this.collapseBtn.setVisible(c.collapsible == true);
35785         }
35786         
35787         this.cmargins = c.cmargins || this.cmargins ||
35788                          (this.position == "west" || this.position == "east" ?
35789                              {top: 0, left: 2, right:2, bottom: 0} :
35790                              {top: 2, left: 0, right:0, bottom: 2});
35791         */
35792         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35793         
35794         
35795         this.bottomTabs = c.tabPosition != "top";
35796         
35797         this.autoScroll = c.autoScroll || false;
35798         
35799         
35800        
35801         
35802         this.duration = c.duration || .30;
35803         this.slideDuration = c.slideDuration || .45;
35804         this.config = c;
35805        
35806     },
35807     /**
35808      * Returns true if this region is currently visible.
35809      * @return {Boolean}
35810      */
35811     isVisible : function(){
35812         return this.visible;
35813     },
35814
35815     /**
35816      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35817      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35818      */
35819     //setCollapsedTitle : function(title){
35820     //    title = title || "&#160;";
35821      //   if(this.collapsedTitleTextEl){
35822       //      this.collapsedTitleTextEl.innerHTML = title;
35823        // }
35824     //},
35825
35826     getBox : function(){
35827         var b;
35828       //  if(!this.collapsed){
35829             b = this.el.getBox(false, true);
35830        // }else{
35831           //  b = this.collapsedEl.getBox(false, true);
35832         //}
35833         return b;
35834     },
35835
35836     getMargins : function(){
35837         return this.margins;
35838         //return this.collapsed ? this.cmargins : this.margins;
35839     },
35840 /*
35841     highlight : function(){
35842         this.el.addClass("x-layout-panel-dragover");
35843     },
35844
35845     unhighlight : function(){
35846         this.el.removeClass("x-layout-panel-dragover");
35847     },
35848 */
35849     updateBox : function(box)
35850     {
35851         if (!this.bodyEl) {
35852             return; // not rendered yet..
35853         }
35854         
35855         this.box = box;
35856         if(!this.collapsed){
35857             this.el.dom.style.left = box.x + "px";
35858             this.el.dom.style.top = box.y + "px";
35859             this.updateBody(box.width, box.height);
35860         }else{
35861             this.collapsedEl.dom.style.left = box.x + "px";
35862             this.collapsedEl.dom.style.top = box.y + "px";
35863             this.collapsedEl.setSize(box.width, box.height);
35864         }
35865         if(this.tabs){
35866             this.tabs.autoSizeTabs();
35867         }
35868     },
35869
35870     updateBody : function(w, h)
35871     {
35872         if(w !== null){
35873             this.el.setWidth(w);
35874             w -= this.el.getBorderWidth("rl");
35875             if(this.config.adjustments){
35876                 w += this.config.adjustments[0];
35877             }
35878         }
35879         if(h !== null && h > 0){
35880             this.el.setHeight(h);
35881             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35882             h -= this.el.getBorderWidth("tb");
35883             if(this.config.adjustments){
35884                 h += this.config.adjustments[1];
35885             }
35886             this.bodyEl.setHeight(h);
35887             if(this.tabs){
35888                 h = this.tabs.syncHeight(h);
35889             }
35890         }
35891         if(this.panelSize){
35892             w = w !== null ? w : this.panelSize.width;
35893             h = h !== null ? h : this.panelSize.height;
35894         }
35895         if(this.activePanel){
35896             var el = this.activePanel.getEl();
35897             w = w !== null ? w : el.getWidth();
35898             h = h !== null ? h : el.getHeight();
35899             this.panelSize = {width: w, height: h};
35900             this.activePanel.setSize(w, h);
35901         }
35902         if(Roo.isIE && this.tabs){
35903             this.tabs.el.repaint();
35904         }
35905     },
35906
35907     /**
35908      * Returns the container element for this region.
35909      * @return {Roo.Element}
35910      */
35911     getEl : function(){
35912         return this.el;
35913     },
35914
35915     /**
35916      * Hides this region.
35917      */
35918     hide : function(){
35919         //if(!this.collapsed){
35920             this.el.dom.style.left = "-2000px";
35921             this.el.hide();
35922         //}else{
35923          //   this.collapsedEl.dom.style.left = "-2000px";
35924          //   this.collapsedEl.hide();
35925        // }
35926         this.visible = false;
35927         this.fireEvent("visibilitychange", this, false);
35928     },
35929
35930     /**
35931      * Shows this region if it was previously hidden.
35932      */
35933     show : function(){
35934         //if(!this.collapsed){
35935             this.el.show();
35936         //}else{
35937         //    this.collapsedEl.show();
35938        // }
35939         this.visible = true;
35940         this.fireEvent("visibilitychange", this, true);
35941     },
35942 /*
35943     closeClicked : function(){
35944         if(this.activePanel){
35945             this.remove(this.activePanel);
35946         }
35947     },
35948
35949     collapseClick : function(e){
35950         if(this.isSlid){
35951            e.stopPropagation();
35952            this.slideIn();
35953         }else{
35954            e.stopPropagation();
35955            this.slideOut();
35956         }
35957     },
35958 */
35959     /**
35960      * Collapses this region.
35961      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35962      */
35963     /*
35964     collapse : function(skipAnim, skipCheck = false){
35965         if(this.collapsed) {
35966             return;
35967         }
35968         
35969         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35970             
35971             this.collapsed = true;
35972             if(this.split){
35973                 this.split.el.hide();
35974             }
35975             if(this.config.animate && skipAnim !== true){
35976                 this.fireEvent("invalidated", this);
35977                 this.animateCollapse();
35978             }else{
35979                 this.el.setLocation(-20000,-20000);
35980                 this.el.hide();
35981                 this.collapsedEl.show();
35982                 this.fireEvent("collapsed", this);
35983                 this.fireEvent("invalidated", this);
35984             }
35985         }
35986         
35987     },
35988 */
35989     animateCollapse : function(){
35990         // overridden
35991     },
35992
35993     /**
35994      * Expands this region if it was previously collapsed.
35995      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35996      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35997      */
35998     /*
35999     expand : function(e, skipAnim){
36000         if(e) {
36001             e.stopPropagation();
36002         }
36003         if(!this.collapsed || this.el.hasActiveFx()) {
36004             return;
36005         }
36006         if(this.isSlid){
36007             this.afterSlideIn();
36008             skipAnim = true;
36009         }
36010         this.collapsed = false;
36011         if(this.config.animate && skipAnim !== true){
36012             this.animateExpand();
36013         }else{
36014             this.el.show();
36015             if(this.split){
36016                 this.split.el.show();
36017             }
36018             this.collapsedEl.setLocation(-2000,-2000);
36019             this.collapsedEl.hide();
36020             this.fireEvent("invalidated", this);
36021             this.fireEvent("expanded", this);
36022         }
36023     },
36024 */
36025     animateExpand : function(){
36026         // overridden
36027     },
36028
36029     initTabs : function()
36030     {
36031         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36032         
36033         var ts = new Roo.bootstrap.panel.Tabs({
36034                 el: this.bodyEl.dom,
36035                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36036                 disableTooltips: this.config.disableTabTips,
36037                 toolbar : this.config.toolbar
36038             });
36039         
36040         if(this.config.hideTabs){
36041             ts.stripWrap.setDisplayed(false);
36042         }
36043         this.tabs = ts;
36044         ts.resizeTabs = this.config.resizeTabs === true;
36045         ts.minTabWidth = this.config.minTabWidth || 40;
36046         ts.maxTabWidth = this.config.maxTabWidth || 250;
36047         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36048         ts.monitorResize = false;
36049         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36050         ts.bodyEl.addClass('roo-layout-tabs-body');
36051         this.panels.each(this.initPanelAsTab, this);
36052     },
36053
36054     initPanelAsTab : function(panel){
36055         var ti = this.tabs.addTab(
36056             panel.getEl().id,
36057             panel.getTitle(),
36058             null,
36059             this.config.closeOnTab && panel.isClosable(),
36060             panel.tpl
36061         );
36062         if(panel.tabTip !== undefined){
36063             ti.setTooltip(panel.tabTip);
36064         }
36065         ti.on("activate", function(){
36066               this.setActivePanel(panel);
36067         }, this);
36068         
36069         if(this.config.closeOnTab){
36070             ti.on("beforeclose", function(t, e){
36071                 e.cancel = true;
36072                 this.remove(panel);
36073             }, this);
36074         }
36075         
36076         panel.tabItem = ti;
36077         
36078         return ti;
36079     },
36080
36081     updatePanelTitle : function(panel, title)
36082     {
36083         if(this.activePanel == panel){
36084             this.updateTitle(title);
36085         }
36086         if(this.tabs){
36087             var ti = this.tabs.getTab(panel.getEl().id);
36088             ti.setText(title);
36089             if(panel.tabTip !== undefined){
36090                 ti.setTooltip(panel.tabTip);
36091             }
36092         }
36093     },
36094
36095     updateTitle : function(title){
36096         if(this.titleTextEl && !this.config.title){
36097             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36098         }
36099     },
36100
36101     setActivePanel : function(panel)
36102     {
36103         panel = this.getPanel(panel);
36104         if(this.activePanel && this.activePanel != panel){
36105             if(this.activePanel.setActiveState(false) === false){
36106                 return;
36107             }
36108         }
36109         this.activePanel = panel;
36110         panel.setActiveState(true);
36111         if(this.panelSize){
36112             panel.setSize(this.panelSize.width, this.panelSize.height);
36113         }
36114         if(this.closeBtn){
36115             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36116         }
36117         this.updateTitle(panel.getTitle());
36118         if(this.tabs){
36119             this.fireEvent("invalidated", this);
36120         }
36121         this.fireEvent("panelactivated", this, panel);
36122     },
36123
36124     /**
36125      * Shows the specified panel.
36126      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36127      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36128      */
36129     showPanel : function(panel)
36130     {
36131         panel = this.getPanel(panel);
36132         if(panel){
36133             if(this.tabs){
36134                 var tab = this.tabs.getTab(panel.getEl().id);
36135                 if(tab.isHidden()){
36136                     this.tabs.unhideTab(tab.id);
36137                 }
36138                 tab.activate();
36139             }else{
36140                 this.setActivePanel(panel);
36141             }
36142         }
36143         return panel;
36144     },
36145
36146     /**
36147      * Get the active panel for this region.
36148      * @return {Roo.ContentPanel} The active panel or null
36149      */
36150     getActivePanel : function(){
36151         return this.activePanel;
36152     },
36153
36154     validateVisibility : function(){
36155         if(this.panels.getCount() < 1){
36156             this.updateTitle("&#160;");
36157             this.closeBtn.hide();
36158             this.hide();
36159         }else{
36160             if(!this.isVisible()){
36161                 this.show();
36162             }
36163         }
36164     },
36165
36166     /**
36167      * Adds the passed ContentPanel(s) to this region.
36168      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36169      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36170      */
36171     add : function(panel)
36172     {
36173         if(arguments.length > 1){
36174             for(var i = 0, len = arguments.length; i < len; i++) {
36175                 this.add(arguments[i]);
36176             }
36177             return null;
36178         }
36179         
36180         // if we have not been rendered yet, then we can not really do much of this..
36181         if (!this.bodyEl) {
36182             this.unrendered_panels.push(panel);
36183             return panel;
36184         }
36185         
36186         
36187         
36188         
36189         if(this.hasPanel(panel)){
36190             this.showPanel(panel);
36191             return panel;
36192         }
36193         panel.setRegion(this);
36194         this.panels.add(panel);
36195        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36196             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36197             // and hide them... ???
36198             this.bodyEl.dom.appendChild(panel.getEl().dom);
36199             if(panel.background !== true){
36200                 this.setActivePanel(panel);
36201             }
36202             this.fireEvent("paneladded", this, panel);
36203             return panel;
36204         }
36205         */
36206         if(!this.tabs){
36207             this.initTabs();
36208         }else{
36209             this.initPanelAsTab(panel);
36210         }
36211         
36212         
36213         if(panel.background !== true){
36214             this.tabs.activate(panel.getEl().id);
36215         }
36216         this.fireEvent("paneladded", this, panel);
36217         return panel;
36218     },
36219
36220     /**
36221      * Hides the tab for the specified panel.
36222      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36223      */
36224     hidePanel : function(panel){
36225         if(this.tabs && (panel = this.getPanel(panel))){
36226             this.tabs.hideTab(panel.getEl().id);
36227         }
36228     },
36229
36230     /**
36231      * Unhides the tab for a previously hidden panel.
36232      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36233      */
36234     unhidePanel : function(panel){
36235         if(this.tabs && (panel = this.getPanel(panel))){
36236             this.tabs.unhideTab(panel.getEl().id);
36237         }
36238     },
36239
36240     clearPanels : function(){
36241         while(this.panels.getCount() > 0){
36242              this.remove(this.panels.first());
36243         }
36244     },
36245
36246     /**
36247      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36248      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36249      * @param {Boolean} preservePanel Overrides the config preservePanel option
36250      * @return {Roo.ContentPanel} The panel that was removed
36251      */
36252     remove : function(panel, preservePanel)
36253     {
36254         panel = this.getPanel(panel);
36255         if(!panel){
36256             return null;
36257         }
36258         var e = {};
36259         this.fireEvent("beforeremove", this, panel, e);
36260         if(e.cancel === true){
36261             return null;
36262         }
36263         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36264         var panelId = panel.getId();
36265         this.panels.removeKey(panelId);
36266         if(preservePanel){
36267             document.body.appendChild(panel.getEl().dom);
36268         }
36269         if(this.tabs){
36270             this.tabs.removeTab(panel.getEl().id);
36271         }else if (!preservePanel){
36272             this.bodyEl.dom.removeChild(panel.getEl().dom);
36273         }
36274         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36275             var p = this.panels.first();
36276             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36277             tempEl.appendChild(p.getEl().dom);
36278             this.bodyEl.update("");
36279             this.bodyEl.dom.appendChild(p.getEl().dom);
36280             tempEl = null;
36281             this.updateTitle(p.getTitle());
36282             this.tabs = null;
36283             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36284             this.setActivePanel(p);
36285         }
36286         panel.setRegion(null);
36287         if(this.activePanel == panel){
36288             this.activePanel = null;
36289         }
36290         if(this.config.autoDestroy !== false && preservePanel !== true){
36291             try{panel.destroy();}catch(e){}
36292         }
36293         this.fireEvent("panelremoved", this, panel);
36294         return panel;
36295     },
36296
36297     /**
36298      * Returns the TabPanel component used by this region
36299      * @return {Roo.TabPanel}
36300      */
36301     getTabs : function(){
36302         return this.tabs;
36303     },
36304
36305     createTool : function(parentEl, className){
36306         var btn = Roo.DomHelper.append(parentEl, {
36307             tag: "div",
36308             cls: "x-layout-tools-button",
36309             children: [ {
36310                 tag: "div",
36311                 cls: "roo-layout-tools-button-inner " + className,
36312                 html: "&#160;"
36313             }]
36314         }, true);
36315         btn.addClassOnOver("roo-layout-tools-button-over");
36316         return btn;
36317     }
36318 });/*
36319  * Based on:
36320  * Ext JS Library 1.1.1
36321  * Copyright(c) 2006-2007, Ext JS, LLC.
36322  *
36323  * Originally Released Under LGPL - original licence link has changed is not relivant.
36324  *
36325  * Fork - LGPL
36326  * <script type="text/javascript">
36327  */
36328  
36329
36330
36331 /**
36332  * @class Roo.SplitLayoutRegion
36333  * @extends Roo.LayoutRegion
36334  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36335  */
36336 Roo.bootstrap.layout.Split = function(config){
36337     this.cursor = config.cursor;
36338     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36339 };
36340
36341 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36342 {
36343     splitTip : "Drag to resize.",
36344     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36345     useSplitTips : false,
36346
36347     applyConfig : function(config){
36348         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36349     },
36350     
36351     onRender : function(ctr,pos) {
36352         
36353         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36354         if(!this.config.split){
36355             return;
36356         }
36357         if(!this.split){
36358             
36359             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36360                             tag: "div",
36361                             id: this.el.id + "-split",
36362                             cls: "roo-layout-split roo-layout-split-"+this.position,
36363                             html: "&#160;"
36364             });
36365             /** The SplitBar for this region 
36366             * @type Roo.SplitBar */
36367             // does not exist yet...
36368             Roo.log([this.position, this.orientation]);
36369             
36370             this.split = new Roo.bootstrap.SplitBar({
36371                 dragElement : splitEl,
36372                 resizingElement: this.el,
36373                 orientation : this.orientation
36374             });
36375             
36376             this.split.on("moved", this.onSplitMove, this);
36377             this.split.useShim = this.config.useShim === true;
36378             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36379             if(this.useSplitTips){
36380                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36381             }
36382             //if(config.collapsible){
36383             //    this.split.el.on("dblclick", this.collapse,  this);
36384             //}
36385         }
36386         if(typeof this.config.minSize != "undefined"){
36387             this.split.minSize = this.config.minSize;
36388         }
36389         if(typeof this.config.maxSize != "undefined"){
36390             this.split.maxSize = this.config.maxSize;
36391         }
36392         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36393             this.hideSplitter();
36394         }
36395         
36396     },
36397
36398     getHMaxSize : function(){
36399          var cmax = this.config.maxSize || 10000;
36400          var center = this.mgr.getRegion("center");
36401          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36402     },
36403
36404     getVMaxSize : function(){
36405          var cmax = this.config.maxSize || 10000;
36406          var center = this.mgr.getRegion("center");
36407          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36408     },
36409
36410     onSplitMove : function(split, newSize){
36411         this.fireEvent("resized", this, newSize);
36412     },
36413     
36414     /** 
36415      * Returns the {@link Roo.SplitBar} for this region.
36416      * @return {Roo.SplitBar}
36417      */
36418     getSplitBar : function(){
36419         return this.split;
36420     },
36421     
36422     hide : function(){
36423         this.hideSplitter();
36424         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36425     },
36426
36427     hideSplitter : function(){
36428         if(this.split){
36429             this.split.el.setLocation(-2000,-2000);
36430             this.split.el.hide();
36431         }
36432     },
36433
36434     show : function(){
36435         if(this.split){
36436             this.split.el.show();
36437         }
36438         Roo.bootstrap.layout.Split.superclass.show.call(this);
36439     },
36440     
36441     beforeSlide: function(){
36442         if(Roo.isGecko){// firefox overflow auto bug workaround
36443             this.bodyEl.clip();
36444             if(this.tabs) {
36445                 this.tabs.bodyEl.clip();
36446             }
36447             if(this.activePanel){
36448                 this.activePanel.getEl().clip();
36449                 
36450                 if(this.activePanel.beforeSlide){
36451                     this.activePanel.beforeSlide();
36452                 }
36453             }
36454         }
36455     },
36456     
36457     afterSlide : function(){
36458         if(Roo.isGecko){// firefox overflow auto bug workaround
36459             this.bodyEl.unclip();
36460             if(this.tabs) {
36461                 this.tabs.bodyEl.unclip();
36462             }
36463             if(this.activePanel){
36464                 this.activePanel.getEl().unclip();
36465                 if(this.activePanel.afterSlide){
36466                     this.activePanel.afterSlide();
36467                 }
36468             }
36469         }
36470     },
36471
36472     initAutoHide : function(){
36473         if(this.autoHide !== false){
36474             if(!this.autoHideHd){
36475                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36476                 this.autoHideHd = {
36477                     "mouseout": function(e){
36478                         if(!e.within(this.el, true)){
36479                             st.delay(500);
36480                         }
36481                     },
36482                     "mouseover" : function(e){
36483                         st.cancel();
36484                     },
36485                     scope : this
36486                 };
36487             }
36488             this.el.on(this.autoHideHd);
36489         }
36490     },
36491
36492     clearAutoHide : function(){
36493         if(this.autoHide !== false){
36494             this.el.un("mouseout", this.autoHideHd.mouseout);
36495             this.el.un("mouseover", this.autoHideHd.mouseover);
36496         }
36497     },
36498
36499     clearMonitor : function(){
36500         Roo.get(document).un("click", this.slideInIf, this);
36501     },
36502
36503     // these names are backwards but not changed for compat
36504     slideOut : function(){
36505         if(this.isSlid || this.el.hasActiveFx()){
36506             return;
36507         }
36508         this.isSlid = true;
36509         if(this.collapseBtn){
36510             this.collapseBtn.hide();
36511         }
36512         this.closeBtnState = this.closeBtn.getStyle('display');
36513         this.closeBtn.hide();
36514         if(this.stickBtn){
36515             this.stickBtn.show();
36516         }
36517         this.el.show();
36518         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36519         this.beforeSlide();
36520         this.el.setStyle("z-index", 10001);
36521         this.el.slideIn(this.getSlideAnchor(), {
36522             callback: function(){
36523                 this.afterSlide();
36524                 this.initAutoHide();
36525                 Roo.get(document).on("click", this.slideInIf, this);
36526                 this.fireEvent("slideshow", this);
36527             },
36528             scope: this,
36529             block: true
36530         });
36531     },
36532
36533     afterSlideIn : function(){
36534         this.clearAutoHide();
36535         this.isSlid = false;
36536         this.clearMonitor();
36537         this.el.setStyle("z-index", "");
36538         if(this.collapseBtn){
36539             this.collapseBtn.show();
36540         }
36541         this.closeBtn.setStyle('display', this.closeBtnState);
36542         if(this.stickBtn){
36543             this.stickBtn.hide();
36544         }
36545         this.fireEvent("slidehide", this);
36546     },
36547
36548     slideIn : function(cb){
36549         if(!this.isSlid || this.el.hasActiveFx()){
36550             Roo.callback(cb);
36551             return;
36552         }
36553         this.isSlid = false;
36554         this.beforeSlide();
36555         this.el.slideOut(this.getSlideAnchor(), {
36556             callback: function(){
36557                 this.el.setLeftTop(-10000, -10000);
36558                 this.afterSlide();
36559                 this.afterSlideIn();
36560                 Roo.callback(cb);
36561             },
36562             scope: this,
36563             block: true
36564         });
36565     },
36566     
36567     slideInIf : function(e){
36568         if(!e.within(this.el)){
36569             this.slideIn();
36570         }
36571     },
36572
36573     animateCollapse : function(){
36574         this.beforeSlide();
36575         this.el.setStyle("z-index", 20000);
36576         var anchor = this.getSlideAnchor();
36577         this.el.slideOut(anchor, {
36578             callback : function(){
36579                 this.el.setStyle("z-index", "");
36580                 this.collapsedEl.slideIn(anchor, {duration:.3});
36581                 this.afterSlide();
36582                 this.el.setLocation(-10000,-10000);
36583                 this.el.hide();
36584                 this.fireEvent("collapsed", this);
36585             },
36586             scope: this,
36587             block: true
36588         });
36589     },
36590
36591     animateExpand : function(){
36592         this.beforeSlide();
36593         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36594         this.el.setStyle("z-index", 20000);
36595         this.collapsedEl.hide({
36596             duration:.1
36597         });
36598         this.el.slideIn(this.getSlideAnchor(), {
36599             callback : function(){
36600                 this.el.setStyle("z-index", "");
36601                 this.afterSlide();
36602                 if(this.split){
36603                     this.split.el.show();
36604                 }
36605                 this.fireEvent("invalidated", this);
36606                 this.fireEvent("expanded", this);
36607             },
36608             scope: this,
36609             block: true
36610         });
36611     },
36612
36613     anchors : {
36614         "west" : "left",
36615         "east" : "right",
36616         "north" : "top",
36617         "south" : "bottom"
36618     },
36619
36620     sanchors : {
36621         "west" : "l",
36622         "east" : "r",
36623         "north" : "t",
36624         "south" : "b"
36625     },
36626
36627     canchors : {
36628         "west" : "tl-tr",
36629         "east" : "tr-tl",
36630         "north" : "tl-bl",
36631         "south" : "bl-tl"
36632     },
36633
36634     getAnchor : function(){
36635         return this.anchors[this.position];
36636     },
36637
36638     getCollapseAnchor : function(){
36639         return this.canchors[this.position];
36640     },
36641
36642     getSlideAnchor : function(){
36643         return this.sanchors[this.position];
36644     },
36645
36646     getAlignAdj : function(){
36647         var cm = this.cmargins;
36648         switch(this.position){
36649             case "west":
36650                 return [0, 0];
36651             break;
36652             case "east":
36653                 return [0, 0];
36654             break;
36655             case "north":
36656                 return [0, 0];
36657             break;
36658             case "south":
36659                 return [0, 0];
36660             break;
36661         }
36662     },
36663
36664     getExpandAdj : function(){
36665         var c = this.collapsedEl, cm = this.cmargins;
36666         switch(this.position){
36667             case "west":
36668                 return [-(cm.right+c.getWidth()+cm.left), 0];
36669             break;
36670             case "east":
36671                 return [cm.right+c.getWidth()+cm.left, 0];
36672             break;
36673             case "north":
36674                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36675             break;
36676             case "south":
36677                 return [0, cm.top+cm.bottom+c.getHeight()];
36678             break;
36679         }
36680     }
36681 });/*
36682  * Based on:
36683  * Ext JS Library 1.1.1
36684  * Copyright(c) 2006-2007, Ext JS, LLC.
36685  *
36686  * Originally Released Under LGPL - original licence link has changed is not relivant.
36687  *
36688  * Fork - LGPL
36689  * <script type="text/javascript">
36690  */
36691 /*
36692  * These classes are private internal classes
36693  */
36694 Roo.bootstrap.layout.Center = function(config){
36695     config.region = "center";
36696     Roo.bootstrap.layout.Region.call(this, config);
36697     this.visible = true;
36698     this.minWidth = config.minWidth || 20;
36699     this.minHeight = config.minHeight || 20;
36700 };
36701
36702 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36703     hide : function(){
36704         // center panel can't be hidden
36705     },
36706     
36707     show : function(){
36708         // center panel can't be hidden
36709     },
36710     
36711     getMinWidth: function(){
36712         return this.minWidth;
36713     },
36714     
36715     getMinHeight: function(){
36716         return this.minHeight;
36717     }
36718 });
36719
36720
36721
36722
36723  
36724
36725
36726
36727
36728
36729 Roo.bootstrap.layout.North = function(config)
36730 {
36731     config.region = 'north';
36732     config.cursor = 'n-resize';
36733     
36734     Roo.bootstrap.layout.Split.call(this, config);
36735     
36736     
36737     if(this.split){
36738         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36739         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36740         this.split.el.addClass("roo-layout-split-v");
36741     }
36742     var size = config.initialSize || config.height;
36743     if(typeof size != "undefined"){
36744         this.el.setHeight(size);
36745     }
36746 };
36747 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36748 {
36749     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36750     
36751     
36752     
36753     getBox : function(){
36754         if(this.collapsed){
36755             return this.collapsedEl.getBox();
36756         }
36757         var box = this.el.getBox();
36758         if(this.split){
36759             box.height += this.split.el.getHeight();
36760         }
36761         return box;
36762     },
36763     
36764     updateBox : function(box){
36765         if(this.split && !this.collapsed){
36766             box.height -= this.split.el.getHeight();
36767             this.split.el.setLeft(box.x);
36768             this.split.el.setTop(box.y+box.height);
36769             this.split.el.setWidth(box.width);
36770         }
36771         if(this.collapsed){
36772             this.updateBody(box.width, null);
36773         }
36774         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36775     }
36776 });
36777
36778
36779
36780
36781
36782 Roo.bootstrap.layout.South = function(config){
36783     config.region = 'south';
36784     config.cursor = 's-resize';
36785     Roo.bootstrap.layout.Split.call(this, config);
36786     if(this.split){
36787         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36788         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36789         this.split.el.addClass("roo-layout-split-v");
36790     }
36791     var size = config.initialSize || config.height;
36792     if(typeof size != "undefined"){
36793         this.el.setHeight(size);
36794     }
36795 };
36796
36797 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36798     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36799     getBox : function(){
36800         if(this.collapsed){
36801             return this.collapsedEl.getBox();
36802         }
36803         var box = this.el.getBox();
36804         if(this.split){
36805             var sh = this.split.el.getHeight();
36806             box.height += sh;
36807             box.y -= sh;
36808         }
36809         return box;
36810     },
36811     
36812     updateBox : function(box){
36813         if(this.split && !this.collapsed){
36814             var sh = this.split.el.getHeight();
36815             box.height -= sh;
36816             box.y += sh;
36817             this.split.el.setLeft(box.x);
36818             this.split.el.setTop(box.y-sh);
36819             this.split.el.setWidth(box.width);
36820         }
36821         if(this.collapsed){
36822             this.updateBody(box.width, null);
36823         }
36824         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36825     }
36826 });
36827
36828 Roo.bootstrap.layout.East = function(config){
36829     config.region = "east";
36830     config.cursor = "e-resize";
36831     Roo.bootstrap.layout.Split.call(this, config);
36832     if(this.split){
36833         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36834         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36835         this.split.el.addClass("roo-layout-split-h");
36836     }
36837     var size = config.initialSize || config.width;
36838     if(typeof size != "undefined"){
36839         this.el.setWidth(size);
36840     }
36841 };
36842 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36843     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36844     getBox : function(){
36845         if(this.collapsed){
36846             return this.collapsedEl.getBox();
36847         }
36848         var box = this.el.getBox();
36849         if(this.split){
36850             var sw = this.split.el.getWidth();
36851             box.width += sw;
36852             box.x -= sw;
36853         }
36854         return box;
36855     },
36856
36857     updateBox : function(box){
36858         if(this.split && !this.collapsed){
36859             var sw = this.split.el.getWidth();
36860             box.width -= sw;
36861             this.split.el.setLeft(box.x);
36862             this.split.el.setTop(box.y);
36863             this.split.el.setHeight(box.height);
36864             box.x += sw;
36865         }
36866         if(this.collapsed){
36867             this.updateBody(null, box.height);
36868         }
36869         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36870     }
36871 });
36872
36873 Roo.bootstrap.layout.West = function(config){
36874     config.region = "west";
36875     config.cursor = "w-resize";
36876     
36877     Roo.bootstrap.layout.Split.call(this, config);
36878     if(this.split){
36879         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36880         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36881         this.split.el.addClass("roo-layout-split-h");
36882     }
36883     
36884 };
36885 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36886     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36887     
36888     onRender: function(ctr, pos)
36889     {
36890         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36891         var size = this.config.initialSize || this.config.width;
36892         if(typeof size != "undefined"){
36893             this.el.setWidth(size);
36894         }
36895     },
36896     
36897     getBox : function(){
36898         if(this.collapsed){
36899             return this.collapsedEl.getBox();
36900         }
36901         var box = this.el.getBox();
36902         if(this.split){
36903             box.width += this.split.el.getWidth();
36904         }
36905         return box;
36906     },
36907     
36908     updateBox : function(box){
36909         if(this.split && !this.collapsed){
36910             var sw = this.split.el.getWidth();
36911             box.width -= sw;
36912             this.split.el.setLeft(box.x+box.width);
36913             this.split.el.setTop(box.y);
36914             this.split.el.setHeight(box.height);
36915         }
36916         if(this.collapsed){
36917             this.updateBody(null, box.height);
36918         }
36919         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36920     }
36921 });
36922 Roo.namespace("Roo.bootstrap.panel");/*
36923  * Based on:
36924  * Ext JS Library 1.1.1
36925  * Copyright(c) 2006-2007, Ext JS, LLC.
36926  *
36927  * Originally Released Under LGPL - original licence link has changed is not relivant.
36928  *
36929  * Fork - LGPL
36930  * <script type="text/javascript">
36931  */
36932 /**
36933  * @class Roo.ContentPanel
36934  * @extends Roo.util.Observable
36935  * A basic ContentPanel element.
36936  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36937  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36938  * @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
36939  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36940  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36941  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36942  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36943  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36944  * @cfg {String} title          The title for this panel
36945  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36946  * @cfg {String} url            Calls {@link #setUrl} with this value
36947  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36948  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36949  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36950  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36951  * @cfg {Boolean} badges render the badges
36952
36953  * @constructor
36954  * Create a new ContentPanel.
36955  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36956  * @param {String/Object} config A string to set only the title or a config object
36957  * @param {String} content (optional) Set the HTML content for this panel
36958  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36959  */
36960 Roo.bootstrap.panel.Content = function( config){
36961     
36962     this.tpl = config.tpl || false;
36963     
36964     var el = config.el;
36965     var content = config.content;
36966
36967     if(config.autoCreate){ // xtype is available if this is called from factory
36968         el = Roo.id();
36969     }
36970     this.el = Roo.get(el);
36971     if(!this.el && config && config.autoCreate){
36972         if(typeof config.autoCreate == "object"){
36973             if(!config.autoCreate.id){
36974                 config.autoCreate.id = config.id||el;
36975             }
36976             this.el = Roo.DomHelper.append(document.body,
36977                         config.autoCreate, true);
36978         }else{
36979             var elcfg =  {   tag: "div",
36980                             cls: "roo-layout-inactive-content",
36981                             id: config.id||el
36982                             };
36983             if (config.html) {
36984                 elcfg.html = config.html;
36985                 
36986             }
36987                         
36988             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36989         }
36990     } 
36991     this.closable = false;
36992     this.loaded = false;
36993     this.active = false;
36994    
36995       
36996     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36997         
36998         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36999         
37000         this.wrapEl = this.el; //this.el.wrap();
37001         var ti = [];
37002         if (config.toolbar.items) {
37003             ti = config.toolbar.items ;
37004             delete config.toolbar.items ;
37005         }
37006         
37007         var nitems = [];
37008         this.toolbar.render(this.wrapEl, 'before');
37009         for(var i =0;i < ti.length;i++) {
37010           //  Roo.log(['add child', items[i]]);
37011             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37012         }
37013         this.toolbar.items = nitems;
37014         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37015         delete config.toolbar;
37016         
37017     }
37018     /*
37019     // xtype created footer. - not sure if will work as we normally have to render first..
37020     if (this.footer && !this.footer.el && this.footer.xtype) {
37021         if (!this.wrapEl) {
37022             this.wrapEl = this.el.wrap();
37023         }
37024     
37025         this.footer.container = this.wrapEl.createChild();
37026          
37027         this.footer = Roo.factory(this.footer, Roo);
37028         
37029     }
37030     */
37031     
37032      if(typeof config == "string"){
37033         this.title = config;
37034     }else{
37035         Roo.apply(this, config);
37036     }
37037     
37038     if(this.resizeEl){
37039         this.resizeEl = Roo.get(this.resizeEl, true);
37040     }else{
37041         this.resizeEl = this.el;
37042     }
37043     // handle view.xtype
37044     
37045  
37046     
37047     
37048     this.addEvents({
37049         /**
37050          * @event activate
37051          * Fires when this panel is activated. 
37052          * @param {Roo.ContentPanel} this
37053          */
37054         "activate" : true,
37055         /**
37056          * @event deactivate
37057          * Fires when this panel is activated. 
37058          * @param {Roo.ContentPanel} this
37059          */
37060         "deactivate" : true,
37061
37062         /**
37063          * @event resize
37064          * Fires when this panel is resized if fitToFrame is true.
37065          * @param {Roo.ContentPanel} this
37066          * @param {Number} width The width after any component adjustments
37067          * @param {Number} height The height after any component adjustments
37068          */
37069         "resize" : true,
37070         
37071          /**
37072          * @event render
37073          * Fires when this tab is created
37074          * @param {Roo.ContentPanel} this
37075          */
37076         "render" : true
37077         
37078         
37079         
37080     });
37081     
37082
37083     
37084     
37085     if(this.autoScroll){
37086         this.resizeEl.setStyle("overflow", "auto");
37087     } else {
37088         // fix randome scrolling
37089         //this.el.on('scroll', function() {
37090         //    Roo.log('fix random scolling');
37091         //    this.scrollTo('top',0); 
37092         //});
37093     }
37094     content = content || this.content;
37095     if(content){
37096         this.setContent(content);
37097     }
37098     if(config && config.url){
37099         this.setUrl(this.url, this.params, this.loadOnce);
37100     }
37101     
37102     
37103     
37104     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37105     
37106     if (this.view && typeof(this.view.xtype) != 'undefined') {
37107         this.view.el = this.el.appendChild(document.createElement("div"));
37108         this.view = Roo.factory(this.view); 
37109         this.view.render  &&  this.view.render(false, '');  
37110     }
37111     
37112     
37113     this.fireEvent('render', this);
37114 };
37115
37116 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37117     
37118     tabTip : '',
37119     
37120     setRegion : function(region){
37121         this.region = region;
37122         this.setActiveClass(region && !this.background);
37123     },
37124     
37125     
37126     setActiveClass: function(state)
37127     {
37128         if(state){
37129            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37130            this.el.setStyle('position','relative');
37131         }else{
37132            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37133            this.el.setStyle('position', 'absolute');
37134         } 
37135     },
37136     
37137     /**
37138      * Returns the toolbar for this Panel if one was configured. 
37139      * @return {Roo.Toolbar} 
37140      */
37141     getToolbar : function(){
37142         return this.toolbar;
37143     },
37144     
37145     setActiveState : function(active)
37146     {
37147         this.active = active;
37148         this.setActiveClass(active);
37149         if(!active){
37150             if(this.fireEvent("deactivate", this) === false){
37151                 return false;
37152             }
37153             return true;
37154         }
37155         this.fireEvent("activate", this);
37156         return true;
37157     },
37158     /**
37159      * Updates this panel's element
37160      * @param {String} content The new content
37161      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37162     */
37163     setContent : function(content, loadScripts){
37164         this.el.update(content, loadScripts);
37165     },
37166
37167     ignoreResize : function(w, h){
37168         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37169             return true;
37170         }else{
37171             this.lastSize = {width: w, height: h};
37172             return false;
37173         }
37174     },
37175     /**
37176      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37177      * @return {Roo.UpdateManager} The UpdateManager
37178      */
37179     getUpdateManager : function(){
37180         return this.el.getUpdateManager();
37181     },
37182      /**
37183      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37184      * @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:
37185 <pre><code>
37186 panel.load({
37187     url: "your-url.php",
37188     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37189     callback: yourFunction,
37190     scope: yourObject, //(optional scope)
37191     discardUrl: false,
37192     nocache: false,
37193     text: "Loading...",
37194     timeout: 30,
37195     scripts: false
37196 });
37197 </code></pre>
37198      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37199      * 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.
37200      * @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}
37201      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37202      * @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.
37203      * @return {Roo.ContentPanel} this
37204      */
37205     load : function(){
37206         var um = this.el.getUpdateManager();
37207         um.update.apply(um, arguments);
37208         return this;
37209     },
37210
37211
37212     /**
37213      * 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.
37214      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37215      * @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)
37216      * @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)
37217      * @return {Roo.UpdateManager} The UpdateManager
37218      */
37219     setUrl : function(url, params, loadOnce){
37220         if(this.refreshDelegate){
37221             this.removeListener("activate", this.refreshDelegate);
37222         }
37223         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37224         this.on("activate", this.refreshDelegate);
37225         return this.el.getUpdateManager();
37226     },
37227     
37228     _handleRefresh : function(url, params, loadOnce){
37229         if(!loadOnce || !this.loaded){
37230             var updater = this.el.getUpdateManager();
37231             updater.update(url, params, this._setLoaded.createDelegate(this));
37232         }
37233     },
37234     
37235     _setLoaded : function(){
37236         this.loaded = true;
37237     }, 
37238     
37239     /**
37240      * Returns this panel's id
37241      * @return {String} 
37242      */
37243     getId : function(){
37244         return this.el.id;
37245     },
37246     
37247     /** 
37248      * Returns this panel's element - used by regiosn to add.
37249      * @return {Roo.Element} 
37250      */
37251     getEl : function(){
37252         return this.wrapEl || this.el;
37253     },
37254     
37255    
37256     
37257     adjustForComponents : function(width, height)
37258     {
37259         //Roo.log('adjustForComponents ');
37260         if(this.resizeEl != this.el){
37261             width -= this.el.getFrameWidth('lr');
37262             height -= this.el.getFrameWidth('tb');
37263         }
37264         if(this.toolbar){
37265             var te = this.toolbar.getEl();
37266             te.setWidth(width);
37267             height -= te.getHeight();
37268         }
37269         if(this.footer){
37270             var te = this.footer.getEl();
37271             te.setWidth(width);
37272             height -= te.getHeight();
37273         }
37274         
37275         
37276         if(this.adjustments){
37277             width += this.adjustments[0];
37278             height += this.adjustments[1];
37279         }
37280         return {"width": width, "height": height};
37281     },
37282     
37283     setSize : function(width, height){
37284         if(this.fitToFrame && !this.ignoreResize(width, height)){
37285             if(this.fitContainer && this.resizeEl != this.el){
37286                 this.el.setSize(width, height);
37287             }
37288             var size = this.adjustForComponents(width, height);
37289             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37290             this.fireEvent('resize', this, size.width, size.height);
37291         }
37292     },
37293     
37294     /**
37295      * Returns this panel's title
37296      * @return {String} 
37297      */
37298     getTitle : function(){
37299         
37300         if (typeof(this.title) != 'object') {
37301             return this.title;
37302         }
37303         
37304         var t = '';
37305         for (var k in this.title) {
37306             if (!this.title.hasOwnProperty(k)) {
37307                 continue;
37308             }
37309             
37310             if (k.indexOf('-') >= 0) {
37311                 var s = k.split('-');
37312                 for (var i = 0; i<s.length; i++) {
37313                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37314                 }
37315             } else {
37316                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37317             }
37318         }
37319         return t;
37320     },
37321     
37322     /**
37323      * Set this panel's title
37324      * @param {String} title
37325      */
37326     setTitle : function(title){
37327         this.title = title;
37328         if(this.region){
37329             this.region.updatePanelTitle(this, title);
37330         }
37331     },
37332     
37333     /**
37334      * Returns true is this panel was configured to be closable
37335      * @return {Boolean} 
37336      */
37337     isClosable : function(){
37338         return this.closable;
37339     },
37340     
37341     beforeSlide : function(){
37342         this.el.clip();
37343         this.resizeEl.clip();
37344     },
37345     
37346     afterSlide : function(){
37347         this.el.unclip();
37348         this.resizeEl.unclip();
37349     },
37350     
37351     /**
37352      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37353      *   Will fail silently if the {@link #setUrl} method has not been called.
37354      *   This does not activate the panel, just updates its content.
37355      */
37356     refresh : function(){
37357         if(this.refreshDelegate){
37358            this.loaded = false;
37359            this.refreshDelegate();
37360         }
37361     },
37362     
37363     /**
37364      * Destroys this panel
37365      */
37366     destroy : function(){
37367         this.el.removeAllListeners();
37368         var tempEl = document.createElement("span");
37369         tempEl.appendChild(this.el.dom);
37370         tempEl.innerHTML = "";
37371         this.el.remove();
37372         this.el = null;
37373     },
37374     
37375     /**
37376      * form - if the content panel contains a form - this is a reference to it.
37377      * @type {Roo.form.Form}
37378      */
37379     form : false,
37380     /**
37381      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37382      *    This contains a reference to it.
37383      * @type {Roo.View}
37384      */
37385     view : false,
37386     
37387       /**
37388      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37389      * <pre><code>
37390
37391 layout.addxtype({
37392        xtype : 'Form',
37393        items: [ .... ]
37394    }
37395 );
37396
37397 </code></pre>
37398      * @param {Object} cfg Xtype definition of item to add.
37399      */
37400     
37401     
37402     getChildContainer: function () {
37403         return this.getEl();
37404     }
37405     
37406     
37407     /*
37408         var  ret = new Roo.factory(cfg);
37409         return ret;
37410         
37411         
37412         // add form..
37413         if (cfg.xtype.match(/^Form$/)) {
37414             
37415             var el;
37416             //if (this.footer) {
37417             //    el = this.footer.container.insertSibling(false, 'before');
37418             //} else {
37419                 el = this.el.createChild();
37420             //}
37421
37422             this.form = new  Roo.form.Form(cfg);
37423             
37424             
37425             if ( this.form.allItems.length) {
37426                 this.form.render(el.dom);
37427             }
37428             return this.form;
37429         }
37430         // should only have one of theses..
37431         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37432             // views.. should not be just added - used named prop 'view''
37433             
37434             cfg.el = this.el.appendChild(document.createElement("div"));
37435             // factory?
37436             
37437             var ret = new Roo.factory(cfg);
37438              
37439              ret.render && ret.render(false, ''); // render blank..
37440             this.view = ret;
37441             return ret;
37442         }
37443         return false;
37444     }
37445     \*/
37446 });
37447  
37448 /**
37449  * @class Roo.bootstrap.panel.Grid
37450  * @extends Roo.bootstrap.panel.Content
37451  * @constructor
37452  * Create a new GridPanel.
37453  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37454  * @param {Object} config A the config object
37455   
37456  */
37457
37458
37459
37460 Roo.bootstrap.panel.Grid = function(config)
37461 {
37462     
37463       
37464     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37465         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37466
37467     config.el = this.wrapper;
37468     //this.el = this.wrapper;
37469     
37470       if (config.container) {
37471         // ctor'ed from a Border/panel.grid
37472         
37473         
37474         this.wrapper.setStyle("overflow", "hidden");
37475         this.wrapper.addClass('roo-grid-container');
37476
37477     }
37478     
37479     
37480     if(config.toolbar){
37481         var tool_el = this.wrapper.createChild();    
37482         this.toolbar = Roo.factory(config.toolbar);
37483         var ti = [];
37484         if (config.toolbar.items) {
37485             ti = config.toolbar.items ;
37486             delete config.toolbar.items ;
37487         }
37488         
37489         var nitems = [];
37490         this.toolbar.render(tool_el);
37491         for(var i =0;i < ti.length;i++) {
37492           //  Roo.log(['add child', items[i]]);
37493             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37494         }
37495         this.toolbar.items = nitems;
37496         
37497         delete config.toolbar;
37498     }
37499     
37500     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37501     config.grid.scrollBody = true;;
37502     config.grid.monitorWindowResize = false; // turn off autosizing
37503     config.grid.autoHeight = false;
37504     config.grid.autoWidth = false;
37505     
37506     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37507     
37508     if (config.background) {
37509         // render grid on panel activation (if panel background)
37510         this.on('activate', function(gp) {
37511             if (!gp.grid.rendered) {
37512                 gp.grid.render(this.wrapper);
37513                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37514             }
37515         });
37516             
37517     } else {
37518         this.grid.render(this.wrapper);
37519         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37520
37521     }
37522     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37523     // ??? needed ??? config.el = this.wrapper;
37524     
37525     
37526     
37527   
37528     // xtype created footer. - not sure if will work as we normally have to render first..
37529     if (this.footer && !this.footer.el && this.footer.xtype) {
37530         
37531         var ctr = this.grid.getView().getFooterPanel(true);
37532         this.footer.dataSource = this.grid.dataSource;
37533         this.footer = Roo.factory(this.footer, Roo);
37534         this.footer.render(ctr);
37535         
37536     }
37537     
37538     
37539     
37540     
37541      
37542 };
37543
37544 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37545     getId : function(){
37546         return this.grid.id;
37547     },
37548     
37549     /**
37550      * Returns the grid for this panel
37551      * @return {Roo.bootstrap.Table} 
37552      */
37553     getGrid : function(){
37554         return this.grid;    
37555     },
37556     
37557     setSize : function(width, height){
37558         if(!this.ignoreResize(width, height)){
37559             var grid = this.grid;
37560             var size = this.adjustForComponents(width, height);
37561             var gridel = grid.getGridEl();
37562             gridel.setSize(size.width, size.height);
37563             /*
37564             var thd = grid.getGridEl().select('thead',true).first();
37565             var tbd = grid.getGridEl().select('tbody', true).first();
37566             if (tbd) {
37567                 tbd.setSize(width, height - thd.getHeight());
37568             }
37569             */
37570             grid.autoSize();
37571         }
37572     },
37573      
37574     
37575     
37576     beforeSlide : function(){
37577         this.grid.getView().scroller.clip();
37578     },
37579     
37580     afterSlide : function(){
37581         this.grid.getView().scroller.unclip();
37582     },
37583     
37584     destroy : function(){
37585         this.grid.destroy();
37586         delete this.grid;
37587         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37588     }
37589 });
37590
37591 /**
37592  * @class Roo.bootstrap.panel.Nest
37593  * @extends Roo.bootstrap.panel.Content
37594  * @constructor
37595  * Create a new Panel, that can contain a layout.Border.
37596  * 
37597  * 
37598  * @param {Roo.BorderLayout} layout The layout for this panel
37599  * @param {String/Object} config A string to set only the title or a config object
37600  */
37601 Roo.bootstrap.panel.Nest = function(config)
37602 {
37603     // construct with only one argument..
37604     /* FIXME - implement nicer consturctors
37605     if (layout.layout) {
37606         config = layout;
37607         layout = config.layout;
37608         delete config.layout;
37609     }
37610     if (layout.xtype && !layout.getEl) {
37611         // then layout needs constructing..
37612         layout = Roo.factory(layout, Roo);
37613     }
37614     */
37615     
37616     config.el =  config.layout.getEl();
37617     
37618     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37619     
37620     config.layout.monitorWindowResize = false; // turn off autosizing
37621     this.layout = config.layout;
37622     this.layout.getEl().addClass("roo-layout-nested-layout");
37623     
37624     
37625     
37626     
37627 };
37628
37629 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37630
37631     setSize : function(width, height){
37632         if(!this.ignoreResize(width, height)){
37633             var size = this.adjustForComponents(width, height);
37634             var el = this.layout.getEl();
37635             if (size.height < 1) {
37636                 el.setWidth(size.width);   
37637             } else {
37638                 el.setSize(size.width, size.height);
37639             }
37640             var touch = el.dom.offsetWidth;
37641             this.layout.layout();
37642             // ie requires a double layout on the first pass
37643             if(Roo.isIE && !this.initialized){
37644                 this.initialized = true;
37645                 this.layout.layout();
37646             }
37647         }
37648     },
37649     
37650     // activate all subpanels if not currently active..
37651     
37652     setActiveState : function(active){
37653         this.active = active;
37654         this.setActiveClass(active);
37655         
37656         if(!active){
37657             this.fireEvent("deactivate", this);
37658             return;
37659         }
37660         
37661         this.fireEvent("activate", this);
37662         // not sure if this should happen before or after..
37663         if (!this.layout) {
37664             return; // should not happen..
37665         }
37666         var reg = false;
37667         for (var r in this.layout.regions) {
37668             reg = this.layout.getRegion(r);
37669             if (reg.getActivePanel()) {
37670                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37671                 reg.setActivePanel(reg.getActivePanel());
37672                 continue;
37673             }
37674             if (!reg.panels.length) {
37675                 continue;
37676             }
37677             reg.showPanel(reg.getPanel(0));
37678         }
37679         
37680         
37681         
37682         
37683     },
37684     
37685     /**
37686      * Returns the nested BorderLayout for this panel
37687      * @return {Roo.BorderLayout} 
37688      */
37689     getLayout : function(){
37690         return this.layout;
37691     },
37692     
37693      /**
37694      * Adds a xtype elements to the layout of the nested panel
37695      * <pre><code>
37696
37697 panel.addxtype({
37698        xtype : 'ContentPanel',
37699        region: 'west',
37700        items: [ .... ]
37701    }
37702 );
37703
37704 panel.addxtype({
37705         xtype : 'NestedLayoutPanel',
37706         region: 'west',
37707         layout: {
37708            center: { },
37709            west: { }   
37710         },
37711         items : [ ... list of content panels or nested layout panels.. ]
37712    }
37713 );
37714 </code></pre>
37715      * @param {Object} cfg Xtype definition of item to add.
37716      */
37717     addxtype : function(cfg) {
37718         return this.layout.addxtype(cfg);
37719     
37720     }
37721 });        /*
37722  * Based on:
37723  * Ext JS Library 1.1.1
37724  * Copyright(c) 2006-2007, Ext JS, LLC.
37725  *
37726  * Originally Released Under LGPL - original licence link has changed is not relivant.
37727  *
37728  * Fork - LGPL
37729  * <script type="text/javascript">
37730  */
37731 /**
37732  * @class Roo.TabPanel
37733  * @extends Roo.util.Observable
37734  * A lightweight tab container.
37735  * <br><br>
37736  * Usage:
37737  * <pre><code>
37738 // basic tabs 1, built from existing content
37739 var tabs = new Roo.TabPanel("tabs1");
37740 tabs.addTab("script", "View Script");
37741 tabs.addTab("markup", "View Markup");
37742 tabs.activate("script");
37743
37744 // more advanced tabs, built from javascript
37745 var jtabs = new Roo.TabPanel("jtabs");
37746 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37747
37748 // set up the UpdateManager
37749 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37750 var updater = tab2.getUpdateManager();
37751 updater.setDefaultUrl("ajax1.htm");
37752 tab2.on('activate', updater.refresh, updater, true);
37753
37754 // Use setUrl for Ajax loading
37755 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37756 tab3.setUrl("ajax2.htm", null, true);
37757
37758 // Disabled tab
37759 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37760 tab4.disable();
37761
37762 jtabs.activate("jtabs-1");
37763  * </code></pre>
37764  * @constructor
37765  * Create a new TabPanel.
37766  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37767  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37768  */
37769 Roo.bootstrap.panel.Tabs = function(config){
37770     /**
37771     * The container element for this TabPanel.
37772     * @type Roo.Element
37773     */
37774     this.el = Roo.get(config.el);
37775     delete config.el;
37776     if(config){
37777         if(typeof config == "boolean"){
37778             this.tabPosition = config ? "bottom" : "top";
37779         }else{
37780             Roo.apply(this, config);
37781         }
37782     }
37783     
37784     if(this.tabPosition == "bottom"){
37785         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37786         this.el.addClass("roo-tabs-bottom");
37787     }
37788     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37789     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37790     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37791     if(Roo.isIE){
37792         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37793     }
37794     if(this.tabPosition != "bottom"){
37795         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37796          * @type Roo.Element
37797          */
37798         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37799         this.el.addClass("roo-tabs-top");
37800     }
37801     this.items = [];
37802
37803     this.bodyEl.setStyle("position", "relative");
37804
37805     this.active = null;
37806     this.activateDelegate = this.activate.createDelegate(this);
37807
37808     this.addEvents({
37809         /**
37810          * @event tabchange
37811          * Fires when the active tab changes
37812          * @param {Roo.TabPanel} this
37813          * @param {Roo.TabPanelItem} activePanel The new active tab
37814          */
37815         "tabchange": true,
37816         /**
37817          * @event beforetabchange
37818          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37819          * @param {Roo.TabPanel} this
37820          * @param {Object} e Set cancel to true on this object to cancel the tab change
37821          * @param {Roo.TabPanelItem} tab The tab being changed to
37822          */
37823         "beforetabchange" : true
37824     });
37825
37826     Roo.EventManager.onWindowResize(this.onResize, this);
37827     this.cpad = this.el.getPadding("lr");
37828     this.hiddenCount = 0;
37829
37830
37831     // toolbar on the tabbar support...
37832     if (this.toolbar) {
37833         alert("no toolbar support yet");
37834         this.toolbar  = false;
37835         /*
37836         var tcfg = this.toolbar;
37837         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37838         this.toolbar = new Roo.Toolbar(tcfg);
37839         if (Roo.isSafari) {
37840             var tbl = tcfg.container.child('table', true);
37841             tbl.setAttribute('width', '100%');
37842         }
37843         */
37844         
37845     }
37846    
37847
37848
37849     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37850 };
37851
37852 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37853     /*
37854      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37855      */
37856     tabPosition : "top",
37857     /*
37858      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37859      */
37860     currentTabWidth : 0,
37861     /*
37862      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37863      */
37864     minTabWidth : 40,
37865     /*
37866      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37867      */
37868     maxTabWidth : 250,
37869     /*
37870      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37871      */
37872     preferredTabWidth : 175,
37873     /*
37874      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37875      */
37876     resizeTabs : false,
37877     /*
37878      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37879      */
37880     monitorResize : true,
37881     /*
37882      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37883      */
37884     toolbar : false,
37885
37886     /**
37887      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37888      * @param {String} id The id of the div to use <b>or create</b>
37889      * @param {String} text The text for the tab
37890      * @param {String} content (optional) Content to put in the TabPanelItem body
37891      * @param {Boolean} closable (optional) True to create a close icon on the tab
37892      * @return {Roo.TabPanelItem} The created TabPanelItem
37893      */
37894     addTab : function(id, text, content, closable, tpl)
37895     {
37896         var item = new Roo.bootstrap.panel.TabItem({
37897             panel: this,
37898             id : id,
37899             text : text,
37900             closable : closable,
37901             tpl : tpl
37902         });
37903         this.addTabItem(item);
37904         if(content){
37905             item.setContent(content);
37906         }
37907         return item;
37908     },
37909
37910     /**
37911      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37912      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37913      * @return {Roo.TabPanelItem}
37914      */
37915     getTab : function(id){
37916         return this.items[id];
37917     },
37918
37919     /**
37920      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37921      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37922      */
37923     hideTab : function(id){
37924         var t = this.items[id];
37925         if(!t.isHidden()){
37926            t.setHidden(true);
37927            this.hiddenCount++;
37928            this.autoSizeTabs();
37929         }
37930     },
37931
37932     /**
37933      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37934      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37935      */
37936     unhideTab : function(id){
37937         var t = this.items[id];
37938         if(t.isHidden()){
37939            t.setHidden(false);
37940            this.hiddenCount--;
37941            this.autoSizeTabs();
37942         }
37943     },
37944
37945     /**
37946      * Adds an existing {@link Roo.TabPanelItem}.
37947      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37948      */
37949     addTabItem : function(item){
37950         this.items[item.id] = item;
37951         this.items.push(item);
37952       //  if(this.resizeTabs){
37953     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37954   //         this.autoSizeTabs();
37955 //        }else{
37956 //            item.autoSize();
37957        // }
37958     },
37959
37960     /**
37961      * Removes a {@link Roo.TabPanelItem}.
37962      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37963      */
37964     removeTab : function(id){
37965         var items = this.items;
37966         var tab = items[id];
37967         if(!tab) { return; }
37968         var index = items.indexOf(tab);
37969         if(this.active == tab && items.length > 1){
37970             var newTab = this.getNextAvailable(index);
37971             if(newTab) {
37972                 newTab.activate();
37973             }
37974         }
37975         this.stripEl.dom.removeChild(tab.pnode.dom);
37976         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37977             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37978         }
37979         items.splice(index, 1);
37980         delete this.items[tab.id];
37981         tab.fireEvent("close", tab);
37982         tab.purgeListeners();
37983         this.autoSizeTabs();
37984     },
37985
37986     getNextAvailable : function(start){
37987         var items = this.items;
37988         var index = start;
37989         // look for a next tab that will slide over to
37990         // replace the one being removed
37991         while(index < items.length){
37992             var item = items[++index];
37993             if(item && !item.isHidden()){
37994                 return item;
37995             }
37996         }
37997         // if one isn't found select the previous tab (on the left)
37998         index = start;
37999         while(index >= 0){
38000             var item = items[--index];
38001             if(item && !item.isHidden()){
38002                 return item;
38003             }
38004         }
38005         return null;
38006     },
38007
38008     /**
38009      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38010      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38011      */
38012     disableTab : function(id){
38013         var tab = this.items[id];
38014         if(tab && this.active != tab){
38015             tab.disable();
38016         }
38017     },
38018
38019     /**
38020      * Enables a {@link Roo.TabPanelItem} that is disabled.
38021      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38022      */
38023     enableTab : function(id){
38024         var tab = this.items[id];
38025         tab.enable();
38026     },
38027
38028     /**
38029      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38030      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38031      * @return {Roo.TabPanelItem} The TabPanelItem.
38032      */
38033     activate : function(id){
38034         var tab = this.items[id];
38035         if(!tab){
38036             return null;
38037         }
38038         if(tab == this.active || tab.disabled){
38039             return tab;
38040         }
38041         var e = {};
38042         this.fireEvent("beforetabchange", this, e, tab);
38043         if(e.cancel !== true && !tab.disabled){
38044             if(this.active){
38045                 this.active.hide();
38046             }
38047             this.active = this.items[id];
38048             this.active.show();
38049             this.fireEvent("tabchange", this, this.active);
38050         }
38051         return tab;
38052     },
38053
38054     /**
38055      * Gets the active {@link Roo.TabPanelItem}.
38056      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38057      */
38058     getActiveTab : function(){
38059         return this.active;
38060     },
38061
38062     /**
38063      * Updates the tab body element to fit the height of the container element
38064      * for overflow scrolling
38065      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38066      */
38067     syncHeight : function(targetHeight){
38068         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38069         var bm = this.bodyEl.getMargins();
38070         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38071         this.bodyEl.setHeight(newHeight);
38072         return newHeight;
38073     },
38074
38075     onResize : function(){
38076         if(this.monitorResize){
38077             this.autoSizeTabs();
38078         }
38079     },
38080
38081     /**
38082      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38083      */
38084     beginUpdate : function(){
38085         this.updating = true;
38086     },
38087
38088     /**
38089      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38090      */
38091     endUpdate : function(){
38092         this.updating = false;
38093         this.autoSizeTabs();
38094     },
38095
38096     /**
38097      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38098      */
38099     autoSizeTabs : function(){
38100         var count = this.items.length;
38101         var vcount = count - this.hiddenCount;
38102         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38103             return;
38104         }
38105         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38106         var availWidth = Math.floor(w / vcount);
38107         var b = this.stripBody;
38108         if(b.getWidth() > w){
38109             var tabs = this.items;
38110             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38111             if(availWidth < this.minTabWidth){
38112                 /*if(!this.sleft){    // incomplete scrolling code
38113                     this.createScrollButtons();
38114                 }
38115                 this.showScroll();
38116                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38117             }
38118         }else{
38119             if(this.currentTabWidth < this.preferredTabWidth){
38120                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38121             }
38122         }
38123     },
38124
38125     /**
38126      * Returns the number of tabs in this TabPanel.
38127      * @return {Number}
38128      */
38129      getCount : function(){
38130          return this.items.length;
38131      },
38132
38133     /**
38134      * Resizes all the tabs to the passed width
38135      * @param {Number} The new width
38136      */
38137     setTabWidth : function(width){
38138         this.currentTabWidth = width;
38139         for(var i = 0, len = this.items.length; i < len; i++) {
38140                 if(!this.items[i].isHidden()) {
38141                 this.items[i].setWidth(width);
38142             }
38143         }
38144     },
38145
38146     /**
38147      * Destroys this TabPanel
38148      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38149      */
38150     destroy : function(removeEl){
38151         Roo.EventManager.removeResizeListener(this.onResize, this);
38152         for(var i = 0, len = this.items.length; i < len; i++){
38153             this.items[i].purgeListeners();
38154         }
38155         if(removeEl === true){
38156             this.el.update("");
38157             this.el.remove();
38158         }
38159     },
38160     
38161     createStrip : function(container)
38162     {
38163         var strip = document.createElement("nav");
38164         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38165         container.appendChild(strip);
38166         return strip;
38167     },
38168     
38169     createStripList : function(strip)
38170     {
38171         // div wrapper for retard IE
38172         // returns the "tr" element.
38173         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38174         //'<div class="x-tabs-strip-wrap">'+
38175           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38176           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38177         return strip.firstChild; //.firstChild.firstChild.firstChild;
38178     },
38179     createBody : function(container)
38180     {
38181         var body = document.createElement("div");
38182         Roo.id(body, "tab-body");
38183         //Roo.fly(body).addClass("x-tabs-body");
38184         Roo.fly(body).addClass("tab-content");
38185         container.appendChild(body);
38186         return body;
38187     },
38188     createItemBody :function(bodyEl, id){
38189         var body = Roo.getDom(id);
38190         if(!body){
38191             body = document.createElement("div");
38192             body.id = id;
38193         }
38194         //Roo.fly(body).addClass("x-tabs-item-body");
38195         Roo.fly(body).addClass("tab-pane");
38196          bodyEl.insertBefore(body, bodyEl.firstChild);
38197         return body;
38198     },
38199     /** @private */
38200     createStripElements :  function(stripEl, text, closable, tpl)
38201     {
38202         var td = document.createElement("li"); // was td..
38203         
38204         
38205         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38206         
38207         
38208         stripEl.appendChild(td);
38209         /*if(closable){
38210             td.className = "x-tabs-closable";
38211             if(!this.closeTpl){
38212                 this.closeTpl = new Roo.Template(
38213                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38214                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38215                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38216                 );
38217             }
38218             var el = this.closeTpl.overwrite(td, {"text": text});
38219             var close = el.getElementsByTagName("div")[0];
38220             var inner = el.getElementsByTagName("em")[0];
38221             return {"el": el, "close": close, "inner": inner};
38222         } else {
38223         */
38224         // not sure what this is..
38225 //            if(!this.tabTpl){
38226                 //this.tabTpl = new Roo.Template(
38227                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38228                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38229                 //);
38230 //                this.tabTpl = new Roo.Template(
38231 //                   '<a href="#">' +
38232 //                   '<span unselectable="on"' +
38233 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38234 //                            ' >{text}</span></a>'
38235 //                );
38236 //                
38237 //            }
38238
38239
38240             var template = tpl || this.tabTpl || false;
38241             
38242             if(!template){
38243                 
38244                 template = new Roo.Template(
38245                    '<a href="#">' +
38246                    '<span unselectable="on"' +
38247                             (this.disableTooltips ? '' : ' title="{text}"') +
38248                             ' >{text}</span></a>'
38249                 );
38250             }
38251             
38252             switch (typeof(template)) {
38253                 case 'object' :
38254                     break;
38255                 case 'string' :
38256                     template = new Roo.Template(template);
38257                     break;
38258                 default :
38259                     break;
38260             }
38261             
38262             var el = template.overwrite(td, {"text": text});
38263             
38264             var inner = el.getElementsByTagName("span")[0];
38265             
38266             return {"el": el, "inner": inner};
38267             
38268     }
38269         
38270     
38271 });
38272
38273 /**
38274  * @class Roo.TabPanelItem
38275  * @extends Roo.util.Observable
38276  * Represents an individual item (tab plus body) in a TabPanel.
38277  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38278  * @param {String} id The id of this TabPanelItem
38279  * @param {String} text The text for the tab of this TabPanelItem
38280  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38281  */
38282 Roo.bootstrap.panel.TabItem = function(config){
38283     /**
38284      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38285      * @type Roo.TabPanel
38286      */
38287     this.tabPanel = config.panel;
38288     /**
38289      * The id for this TabPanelItem
38290      * @type String
38291      */
38292     this.id = config.id;
38293     /** @private */
38294     this.disabled = false;
38295     /** @private */
38296     this.text = config.text;
38297     /** @private */
38298     this.loaded = false;
38299     this.closable = config.closable;
38300
38301     /**
38302      * The body element for this TabPanelItem.
38303      * @type Roo.Element
38304      */
38305     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38306     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38307     this.bodyEl.setStyle("display", "block");
38308     this.bodyEl.setStyle("zoom", "1");
38309     //this.hideAction();
38310
38311     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38312     /** @private */
38313     this.el = Roo.get(els.el);
38314     this.inner = Roo.get(els.inner, true);
38315     this.textEl = Roo.get(this.el.dom.firstChild, true);
38316     this.pnode = Roo.get(els.el.parentNode, true);
38317 //    this.el.on("mousedown", this.onTabMouseDown, this);
38318     this.el.on("click", this.onTabClick, this);
38319     /** @private */
38320     if(config.closable){
38321         var c = Roo.get(els.close, true);
38322         c.dom.title = this.closeText;
38323         c.addClassOnOver("close-over");
38324         c.on("click", this.closeClick, this);
38325      }
38326
38327     this.addEvents({
38328          /**
38329          * @event activate
38330          * Fires when this tab becomes the active tab.
38331          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38332          * @param {Roo.TabPanelItem} this
38333          */
38334         "activate": true,
38335         /**
38336          * @event beforeclose
38337          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38338          * @param {Roo.TabPanelItem} this
38339          * @param {Object} e Set cancel to true on this object to cancel the close.
38340          */
38341         "beforeclose": true,
38342         /**
38343          * @event close
38344          * Fires when this tab is closed.
38345          * @param {Roo.TabPanelItem} this
38346          */
38347          "close": true,
38348         /**
38349          * @event deactivate
38350          * Fires when this tab is no longer the active tab.
38351          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38352          * @param {Roo.TabPanelItem} this
38353          */
38354          "deactivate" : true
38355     });
38356     this.hidden = false;
38357
38358     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38359 };
38360
38361 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38362            {
38363     purgeListeners : function(){
38364        Roo.util.Observable.prototype.purgeListeners.call(this);
38365        this.el.removeAllListeners();
38366     },
38367     /**
38368      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38369      */
38370     show : function(){
38371         this.pnode.addClass("active");
38372         this.showAction();
38373         if(Roo.isOpera){
38374             this.tabPanel.stripWrap.repaint();
38375         }
38376         this.fireEvent("activate", this.tabPanel, this);
38377     },
38378
38379     /**
38380      * Returns true if this tab is the active tab.
38381      * @return {Boolean}
38382      */
38383     isActive : function(){
38384         return this.tabPanel.getActiveTab() == this;
38385     },
38386
38387     /**
38388      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38389      */
38390     hide : function(){
38391         this.pnode.removeClass("active");
38392         this.hideAction();
38393         this.fireEvent("deactivate", this.tabPanel, this);
38394     },
38395
38396     hideAction : function(){
38397         this.bodyEl.hide();
38398         this.bodyEl.setStyle("position", "absolute");
38399         this.bodyEl.setLeft("-20000px");
38400         this.bodyEl.setTop("-20000px");
38401     },
38402
38403     showAction : function(){
38404         this.bodyEl.setStyle("position", "relative");
38405         this.bodyEl.setTop("");
38406         this.bodyEl.setLeft("");
38407         this.bodyEl.show();
38408     },
38409
38410     /**
38411      * Set the tooltip for the tab.
38412      * @param {String} tooltip The tab's tooltip
38413      */
38414     setTooltip : function(text){
38415         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38416             this.textEl.dom.qtip = text;
38417             this.textEl.dom.removeAttribute('title');
38418         }else{
38419             this.textEl.dom.title = text;
38420         }
38421     },
38422
38423     onTabClick : function(e){
38424         e.preventDefault();
38425         this.tabPanel.activate(this.id);
38426     },
38427
38428     onTabMouseDown : function(e){
38429         e.preventDefault();
38430         this.tabPanel.activate(this.id);
38431     },
38432 /*
38433     getWidth : function(){
38434         return this.inner.getWidth();
38435     },
38436
38437     setWidth : function(width){
38438         var iwidth = width - this.pnode.getPadding("lr");
38439         this.inner.setWidth(iwidth);
38440         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38441         this.pnode.setWidth(width);
38442     },
38443 */
38444     /**
38445      * Show or hide the tab
38446      * @param {Boolean} hidden True to hide or false to show.
38447      */
38448     setHidden : function(hidden){
38449         this.hidden = hidden;
38450         this.pnode.setStyle("display", hidden ? "none" : "");
38451     },
38452
38453     /**
38454      * Returns true if this tab is "hidden"
38455      * @return {Boolean}
38456      */
38457     isHidden : function(){
38458         return this.hidden;
38459     },
38460
38461     /**
38462      * Returns the text for this tab
38463      * @return {String}
38464      */
38465     getText : function(){
38466         return this.text;
38467     },
38468     /*
38469     autoSize : function(){
38470         //this.el.beginMeasure();
38471         this.textEl.setWidth(1);
38472         /*
38473          *  #2804 [new] Tabs in Roojs
38474          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38475          */
38476         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38477         //this.el.endMeasure();
38478     //},
38479
38480     /**
38481      * Sets the text for the tab (Note: this also sets the tooltip text)
38482      * @param {String} text The tab's text and tooltip
38483      */
38484     setText : function(text){
38485         this.text = text;
38486         this.textEl.update(text);
38487         this.setTooltip(text);
38488         //if(!this.tabPanel.resizeTabs){
38489         //    this.autoSize();
38490         //}
38491     },
38492     /**
38493      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38494      */
38495     activate : function(){
38496         this.tabPanel.activate(this.id);
38497     },
38498
38499     /**
38500      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38501      */
38502     disable : function(){
38503         if(this.tabPanel.active != this){
38504             this.disabled = true;
38505             this.pnode.addClass("disabled");
38506         }
38507     },
38508
38509     /**
38510      * Enables this TabPanelItem if it was previously disabled.
38511      */
38512     enable : function(){
38513         this.disabled = false;
38514         this.pnode.removeClass("disabled");
38515     },
38516
38517     /**
38518      * Sets the content for this TabPanelItem.
38519      * @param {String} content The content
38520      * @param {Boolean} loadScripts true to look for and load scripts
38521      */
38522     setContent : function(content, loadScripts){
38523         this.bodyEl.update(content, loadScripts);
38524     },
38525
38526     /**
38527      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38528      * @return {Roo.UpdateManager} The UpdateManager
38529      */
38530     getUpdateManager : function(){
38531         return this.bodyEl.getUpdateManager();
38532     },
38533
38534     /**
38535      * Set a URL to be used to load the content for this TabPanelItem.
38536      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38537      * @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)
38538      * @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)
38539      * @return {Roo.UpdateManager} The UpdateManager
38540      */
38541     setUrl : function(url, params, loadOnce){
38542         if(this.refreshDelegate){
38543             this.un('activate', this.refreshDelegate);
38544         }
38545         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38546         this.on("activate", this.refreshDelegate);
38547         return this.bodyEl.getUpdateManager();
38548     },
38549
38550     /** @private */
38551     _handleRefresh : function(url, params, loadOnce){
38552         if(!loadOnce || !this.loaded){
38553             var updater = this.bodyEl.getUpdateManager();
38554             updater.update(url, params, this._setLoaded.createDelegate(this));
38555         }
38556     },
38557
38558     /**
38559      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38560      *   Will fail silently if the setUrl method has not been called.
38561      *   This does not activate the panel, just updates its content.
38562      */
38563     refresh : function(){
38564         if(this.refreshDelegate){
38565            this.loaded = false;
38566            this.refreshDelegate();
38567         }
38568     },
38569
38570     /** @private */
38571     _setLoaded : function(){
38572         this.loaded = true;
38573     },
38574
38575     /** @private */
38576     closeClick : function(e){
38577         var o = {};
38578         e.stopEvent();
38579         this.fireEvent("beforeclose", this, o);
38580         if(o.cancel !== true){
38581             this.tabPanel.removeTab(this.id);
38582         }
38583     },
38584     /**
38585      * The text displayed in the tooltip for the close icon.
38586      * @type String
38587      */
38588     closeText : "Close this tab"
38589 });
38590 /**
38591 *    This script refer to:
38592 *    Title: International Telephone Input
38593 *    Author: Jack O'Connor
38594 *    Code version:  v12.1.12
38595 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38596 **/
38597
38598 Roo.bootstrap.PhoneInputData = function() {
38599     var d = [
38600       [
38601         "Afghanistan (‫افغانستان‬‎)",
38602         "af",
38603         "93"
38604       ],
38605       [
38606         "Albania (Shqipëri)",
38607         "al",
38608         "355"
38609       ],
38610       [
38611         "Algeria (‫الجزائر‬‎)",
38612         "dz",
38613         "213"
38614       ],
38615       [
38616         "American Samoa",
38617         "as",
38618         "1684"
38619       ],
38620       [
38621         "Andorra",
38622         "ad",
38623         "376"
38624       ],
38625       [
38626         "Angola",
38627         "ao",
38628         "244"
38629       ],
38630       [
38631         "Anguilla",
38632         "ai",
38633         "1264"
38634       ],
38635       [
38636         "Antigua and Barbuda",
38637         "ag",
38638         "1268"
38639       ],
38640       [
38641         "Argentina",
38642         "ar",
38643         "54"
38644       ],
38645       [
38646         "Armenia (Հայաստան)",
38647         "am",
38648         "374"
38649       ],
38650       [
38651         "Aruba",
38652         "aw",
38653         "297"
38654       ],
38655       [
38656         "Australia",
38657         "au",
38658         "61",
38659         0
38660       ],
38661       [
38662         "Austria (Österreich)",
38663         "at",
38664         "43"
38665       ],
38666       [
38667         "Azerbaijan (Azərbaycan)",
38668         "az",
38669         "994"
38670       ],
38671       [
38672         "Bahamas",
38673         "bs",
38674         "1242"
38675       ],
38676       [
38677         "Bahrain (‫البحرين‬‎)",
38678         "bh",
38679         "973"
38680       ],
38681       [
38682         "Bangladesh (বাংলাদেশ)",
38683         "bd",
38684         "880"
38685       ],
38686       [
38687         "Barbados",
38688         "bb",
38689         "1246"
38690       ],
38691       [
38692         "Belarus (Беларусь)",
38693         "by",
38694         "375"
38695       ],
38696       [
38697         "Belgium (België)",
38698         "be",
38699         "32"
38700       ],
38701       [
38702         "Belize",
38703         "bz",
38704         "501"
38705       ],
38706       [
38707         "Benin (Bénin)",
38708         "bj",
38709         "229"
38710       ],
38711       [
38712         "Bermuda",
38713         "bm",
38714         "1441"
38715       ],
38716       [
38717         "Bhutan (འབྲུག)",
38718         "bt",
38719         "975"
38720       ],
38721       [
38722         "Bolivia",
38723         "bo",
38724         "591"
38725       ],
38726       [
38727         "Bosnia and Herzegovina (Босна и Херцеговина)",
38728         "ba",
38729         "387"
38730       ],
38731       [
38732         "Botswana",
38733         "bw",
38734         "267"
38735       ],
38736       [
38737         "Brazil (Brasil)",
38738         "br",
38739         "55"
38740       ],
38741       [
38742         "British Indian Ocean Territory",
38743         "io",
38744         "246"
38745       ],
38746       [
38747         "British Virgin Islands",
38748         "vg",
38749         "1284"
38750       ],
38751       [
38752         "Brunei",
38753         "bn",
38754         "673"
38755       ],
38756       [
38757         "Bulgaria (България)",
38758         "bg",
38759         "359"
38760       ],
38761       [
38762         "Burkina Faso",
38763         "bf",
38764         "226"
38765       ],
38766       [
38767         "Burundi (Uburundi)",
38768         "bi",
38769         "257"
38770       ],
38771       [
38772         "Cambodia (កម្ពុជា)",
38773         "kh",
38774         "855"
38775       ],
38776       [
38777         "Cameroon (Cameroun)",
38778         "cm",
38779         "237"
38780       ],
38781       [
38782         "Canada",
38783         "ca",
38784         "1",
38785         1,
38786         ["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"]
38787       ],
38788       [
38789         "Cape Verde (Kabu Verdi)",
38790         "cv",
38791         "238"
38792       ],
38793       [
38794         "Caribbean Netherlands",
38795         "bq",
38796         "599",
38797         1
38798       ],
38799       [
38800         "Cayman Islands",
38801         "ky",
38802         "1345"
38803       ],
38804       [
38805         "Central African Republic (République centrafricaine)",
38806         "cf",
38807         "236"
38808       ],
38809       [
38810         "Chad (Tchad)",
38811         "td",
38812         "235"
38813       ],
38814       [
38815         "Chile",
38816         "cl",
38817         "56"
38818       ],
38819       [
38820         "China (中国)",
38821         "cn",
38822         "86"
38823       ],
38824       [
38825         "Christmas Island",
38826         "cx",
38827         "61",
38828         2
38829       ],
38830       [
38831         "Cocos (Keeling) Islands",
38832         "cc",
38833         "61",
38834         1
38835       ],
38836       [
38837         "Colombia",
38838         "co",
38839         "57"
38840       ],
38841       [
38842         "Comoros (‫جزر القمر‬‎)",
38843         "km",
38844         "269"
38845       ],
38846       [
38847         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38848         "cd",
38849         "243"
38850       ],
38851       [
38852         "Congo (Republic) (Congo-Brazzaville)",
38853         "cg",
38854         "242"
38855       ],
38856       [
38857         "Cook Islands",
38858         "ck",
38859         "682"
38860       ],
38861       [
38862         "Costa Rica",
38863         "cr",
38864         "506"
38865       ],
38866       [
38867         "Côte d’Ivoire",
38868         "ci",
38869         "225"
38870       ],
38871       [
38872         "Croatia (Hrvatska)",
38873         "hr",
38874         "385"
38875       ],
38876       [
38877         "Cuba",
38878         "cu",
38879         "53"
38880       ],
38881       [
38882         "Curaçao",
38883         "cw",
38884         "599",
38885         0
38886       ],
38887       [
38888         "Cyprus (Κύπρος)",
38889         "cy",
38890         "357"
38891       ],
38892       [
38893         "Czech Republic (Česká republika)",
38894         "cz",
38895         "420"
38896       ],
38897       [
38898         "Denmark (Danmark)",
38899         "dk",
38900         "45"
38901       ],
38902       [
38903         "Djibouti",
38904         "dj",
38905         "253"
38906       ],
38907       [
38908         "Dominica",
38909         "dm",
38910         "1767"
38911       ],
38912       [
38913         "Dominican Republic (República Dominicana)",
38914         "do",
38915         "1",
38916         2,
38917         ["809", "829", "849"]
38918       ],
38919       [
38920         "Ecuador",
38921         "ec",
38922         "593"
38923       ],
38924       [
38925         "Egypt (‫مصر‬‎)",
38926         "eg",
38927         "20"
38928       ],
38929       [
38930         "El Salvador",
38931         "sv",
38932         "503"
38933       ],
38934       [
38935         "Equatorial Guinea (Guinea Ecuatorial)",
38936         "gq",
38937         "240"
38938       ],
38939       [
38940         "Eritrea",
38941         "er",
38942         "291"
38943       ],
38944       [
38945         "Estonia (Eesti)",
38946         "ee",
38947         "372"
38948       ],
38949       [
38950         "Ethiopia",
38951         "et",
38952         "251"
38953       ],
38954       [
38955         "Falkland Islands (Islas Malvinas)",
38956         "fk",
38957         "500"
38958       ],
38959       [
38960         "Faroe Islands (Føroyar)",
38961         "fo",
38962         "298"
38963       ],
38964       [
38965         "Fiji",
38966         "fj",
38967         "679"
38968       ],
38969       [
38970         "Finland (Suomi)",
38971         "fi",
38972         "358",
38973         0
38974       ],
38975       [
38976         "France",
38977         "fr",
38978         "33"
38979       ],
38980       [
38981         "French Guiana (Guyane française)",
38982         "gf",
38983         "594"
38984       ],
38985       [
38986         "French Polynesia (Polynésie française)",
38987         "pf",
38988         "689"
38989       ],
38990       [
38991         "Gabon",
38992         "ga",
38993         "241"
38994       ],
38995       [
38996         "Gambia",
38997         "gm",
38998         "220"
38999       ],
39000       [
39001         "Georgia (საქართველო)",
39002         "ge",
39003         "995"
39004       ],
39005       [
39006         "Germany (Deutschland)",
39007         "de",
39008         "49"
39009       ],
39010       [
39011         "Ghana (Gaana)",
39012         "gh",
39013         "233"
39014       ],
39015       [
39016         "Gibraltar",
39017         "gi",
39018         "350"
39019       ],
39020       [
39021         "Greece (Ελλάδα)",
39022         "gr",
39023         "30"
39024       ],
39025       [
39026         "Greenland (Kalaallit Nunaat)",
39027         "gl",
39028         "299"
39029       ],
39030       [
39031         "Grenada",
39032         "gd",
39033         "1473"
39034       ],
39035       [
39036         "Guadeloupe",
39037         "gp",
39038         "590",
39039         0
39040       ],
39041       [
39042         "Guam",
39043         "gu",
39044         "1671"
39045       ],
39046       [
39047         "Guatemala",
39048         "gt",
39049         "502"
39050       ],
39051       [
39052         "Guernsey",
39053         "gg",
39054         "44",
39055         1
39056       ],
39057       [
39058         "Guinea (Guinée)",
39059         "gn",
39060         "224"
39061       ],
39062       [
39063         "Guinea-Bissau (Guiné Bissau)",
39064         "gw",
39065         "245"
39066       ],
39067       [
39068         "Guyana",
39069         "gy",
39070         "592"
39071       ],
39072       [
39073         "Haiti",
39074         "ht",
39075         "509"
39076       ],
39077       [
39078         "Honduras",
39079         "hn",
39080         "504"
39081       ],
39082       [
39083         "Hong Kong (香港)",
39084         "hk",
39085         "852"
39086       ],
39087       [
39088         "Hungary (Magyarország)",
39089         "hu",
39090         "36"
39091       ],
39092       [
39093         "Iceland (Ísland)",
39094         "is",
39095         "354"
39096       ],
39097       [
39098         "India (भारत)",
39099         "in",
39100         "91"
39101       ],
39102       [
39103         "Indonesia",
39104         "id",
39105         "62"
39106       ],
39107       [
39108         "Iran (‫ایران‬‎)",
39109         "ir",
39110         "98"
39111       ],
39112       [
39113         "Iraq (‫العراق‬‎)",
39114         "iq",
39115         "964"
39116       ],
39117       [
39118         "Ireland",
39119         "ie",
39120         "353"
39121       ],
39122       [
39123         "Isle of Man",
39124         "im",
39125         "44",
39126         2
39127       ],
39128       [
39129         "Israel (‫ישראל‬‎)",
39130         "il",
39131         "972"
39132       ],
39133       [
39134         "Italy (Italia)",
39135         "it",
39136         "39",
39137         0
39138       ],
39139       [
39140         "Jamaica",
39141         "jm",
39142         "1876"
39143       ],
39144       [
39145         "Japan (日本)",
39146         "jp",
39147         "81"
39148       ],
39149       [
39150         "Jersey",
39151         "je",
39152         "44",
39153         3
39154       ],
39155       [
39156         "Jordan (‫الأردن‬‎)",
39157         "jo",
39158         "962"
39159       ],
39160       [
39161         "Kazakhstan (Казахстан)",
39162         "kz",
39163         "7",
39164         1
39165       ],
39166       [
39167         "Kenya",
39168         "ke",
39169         "254"
39170       ],
39171       [
39172         "Kiribati",
39173         "ki",
39174         "686"
39175       ],
39176       [
39177         "Kosovo",
39178         "xk",
39179         "383"
39180       ],
39181       [
39182         "Kuwait (‫الكويت‬‎)",
39183         "kw",
39184         "965"
39185       ],
39186       [
39187         "Kyrgyzstan (Кыргызстан)",
39188         "kg",
39189         "996"
39190       ],
39191       [
39192         "Laos (ລາວ)",
39193         "la",
39194         "856"
39195       ],
39196       [
39197         "Latvia (Latvija)",
39198         "lv",
39199         "371"
39200       ],
39201       [
39202         "Lebanon (‫لبنان‬‎)",
39203         "lb",
39204         "961"
39205       ],
39206       [
39207         "Lesotho",
39208         "ls",
39209         "266"
39210       ],
39211       [
39212         "Liberia",
39213         "lr",
39214         "231"
39215       ],
39216       [
39217         "Libya (‫ليبيا‬‎)",
39218         "ly",
39219         "218"
39220       ],
39221       [
39222         "Liechtenstein",
39223         "li",
39224         "423"
39225       ],
39226       [
39227         "Lithuania (Lietuva)",
39228         "lt",
39229         "370"
39230       ],
39231       [
39232         "Luxembourg",
39233         "lu",
39234         "352"
39235       ],
39236       [
39237         "Macau (澳門)",
39238         "mo",
39239         "853"
39240       ],
39241       [
39242         "Macedonia (FYROM) (Македонија)",
39243         "mk",
39244         "389"
39245       ],
39246       [
39247         "Madagascar (Madagasikara)",
39248         "mg",
39249         "261"
39250       ],
39251       [
39252         "Malawi",
39253         "mw",
39254         "265"
39255       ],
39256       [
39257         "Malaysia",
39258         "my",
39259         "60"
39260       ],
39261       [
39262         "Maldives",
39263         "mv",
39264         "960"
39265       ],
39266       [
39267         "Mali",
39268         "ml",
39269         "223"
39270       ],
39271       [
39272         "Malta",
39273         "mt",
39274         "356"
39275       ],
39276       [
39277         "Marshall Islands",
39278         "mh",
39279         "692"
39280       ],
39281       [
39282         "Martinique",
39283         "mq",
39284         "596"
39285       ],
39286       [
39287         "Mauritania (‫موريتانيا‬‎)",
39288         "mr",
39289         "222"
39290       ],
39291       [
39292         "Mauritius (Moris)",
39293         "mu",
39294         "230"
39295       ],
39296       [
39297         "Mayotte",
39298         "yt",
39299         "262",
39300         1
39301       ],
39302       [
39303         "Mexico (México)",
39304         "mx",
39305         "52"
39306       ],
39307       [
39308         "Micronesia",
39309         "fm",
39310         "691"
39311       ],
39312       [
39313         "Moldova (Republica Moldova)",
39314         "md",
39315         "373"
39316       ],
39317       [
39318         "Monaco",
39319         "mc",
39320         "377"
39321       ],
39322       [
39323         "Mongolia (Монгол)",
39324         "mn",
39325         "976"
39326       ],
39327       [
39328         "Montenegro (Crna Gora)",
39329         "me",
39330         "382"
39331       ],
39332       [
39333         "Montserrat",
39334         "ms",
39335         "1664"
39336       ],
39337       [
39338         "Morocco (‫المغرب‬‎)",
39339         "ma",
39340         "212",
39341         0
39342       ],
39343       [
39344         "Mozambique (Moçambique)",
39345         "mz",
39346         "258"
39347       ],
39348       [
39349         "Myanmar (Burma) (မြန်မာ)",
39350         "mm",
39351         "95"
39352       ],
39353       [
39354         "Namibia (Namibië)",
39355         "na",
39356         "264"
39357       ],
39358       [
39359         "Nauru",
39360         "nr",
39361         "674"
39362       ],
39363       [
39364         "Nepal (नेपाल)",
39365         "np",
39366         "977"
39367       ],
39368       [
39369         "Netherlands (Nederland)",
39370         "nl",
39371         "31"
39372       ],
39373       [
39374         "New Caledonia (Nouvelle-Calédonie)",
39375         "nc",
39376         "687"
39377       ],
39378       [
39379         "New Zealand",
39380         "nz",
39381         "64"
39382       ],
39383       [
39384         "Nicaragua",
39385         "ni",
39386         "505"
39387       ],
39388       [
39389         "Niger (Nijar)",
39390         "ne",
39391         "227"
39392       ],
39393       [
39394         "Nigeria",
39395         "ng",
39396         "234"
39397       ],
39398       [
39399         "Niue",
39400         "nu",
39401         "683"
39402       ],
39403       [
39404         "Norfolk Island",
39405         "nf",
39406         "672"
39407       ],
39408       [
39409         "North Korea (조선 민주주의 인민 공화국)",
39410         "kp",
39411         "850"
39412       ],
39413       [
39414         "Northern Mariana Islands",
39415         "mp",
39416         "1670"
39417       ],
39418       [
39419         "Norway (Norge)",
39420         "no",
39421         "47",
39422         0
39423       ],
39424       [
39425         "Oman (‫عُمان‬‎)",
39426         "om",
39427         "968"
39428       ],
39429       [
39430         "Pakistan (‫پاکستان‬‎)",
39431         "pk",
39432         "92"
39433       ],
39434       [
39435         "Palau",
39436         "pw",
39437         "680"
39438       ],
39439       [
39440         "Palestine (‫فلسطين‬‎)",
39441         "ps",
39442         "970"
39443       ],
39444       [
39445         "Panama (Panamá)",
39446         "pa",
39447         "507"
39448       ],
39449       [
39450         "Papua New Guinea",
39451         "pg",
39452         "675"
39453       ],
39454       [
39455         "Paraguay",
39456         "py",
39457         "595"
39458       ],
39459       [
39460         "Peru (Perú)",
39461         "pe",
39462         "51"
39463       ],
39464       [
39465         "Philippines",
39466         "ph",
39467         "63"
39468       ],
39469       [
39470         "Poland (Polska)",
39471         "pl",
39472         "48"
39473       ],
39474       [
39475         "Portugal",
39476         "pt",
39477         "351"
39478       ],
39479       [
39480         "Puerto Rico",
39481         "pr",
39482         "1",
39483         3,
39484         ["787", "939"]
39485       ],
39486       [
39487         "Qatar (‫قطر‬‎)",
39488         "qa",
39489         "974"
39490       ],
39491       [
39492         "Réunion (La Réunion)",
39493         "re",
39494         "262",
39495         0
39496       ],
39497       [
39498         "Romania (România)",
39499         "ro",
39500         "40"
39501       ],
39502       [
39503         "Russia (Россия)",
39504         "ru",
39505         "7",
39506         0
39507       ],
39508       [
39509         "Rwanda",
39510         "rw",
39511         "250"
39512       ],
39513       [
39514         "Saint Barthélemy",
39515         "bl",
39516         "590",
39517         1
39518       ],
39519       [
39520         "Saint Helena",
39521         "sh",
39522         "290"
39523       ],
39524       [
39525         "Saint Kitts and Nevis",
39526         "kn",
39527         "1869"
39528       ],
39529       [
39530         "Saint Lucia",
39531         "lc",
39532         "1758"
39533       ],
39534       [
39535         "Saint Martin (Saint-Martin (partie française))",
39536         "mf",
39537         "590",
39538         2
39539       ],
39540       [
39541         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39542         "pm",
39543         "508"
39544       ],
39545       [
39546         "Saint Vincent and the Grenadines",
39547         "vc",
39548         "1784"
39549       ],
39550       [
39551         "Samoa",
39552         "ws",
39553         "685"
39554       ],
39555       [
39556         "San Marino",
39557         "sm",
39558         "378"
39559       ],
39560       [
39561         "São Tomé and Príncipe (São Tomé e Príncipe)",
39562         "st",
39563         "239"
39564       ],
39565       [
39566         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39567         "sa",
39568         "966"
39569       ],
39570       [
39571         "Senegal (Sénégal)",
39572         "sn",
39573         "221"
39574       ],
39575       [
39576         "Serbia (Србија)",
39577         "rs",
39578         "381"
39579       ],
39580       [
39581         "Seychelles",
39582         "sc",
39583         "248"
39584       ],
39585       [
39586         "Sierra Leone",
39587         "sl",
39588         "232"
39589       ],
39590       [
39591         "Singapore",
39592         "sg",
39593         "65"
39594       ],
39595       [
39596         "Sint Maarten",
39597         "sx",
39598         "1721"
39599       ],
39600       [
39601         "Slovakia (Slovensko)",
39602         "sk",
39603         "421"
39604       ],
39605       [
39606         "Slovenia (Slovenija)",
39607         "si",
39608         "386"
39609       ],
39610       [
39611         "Solomon Islands",
39612         "sb",
39613         "677"
39614       ],
39615       [
39616         "Somalia (Soomaaliya)",
39617         "so",
39618         "252"
39619       ],
39620       [
39621         "South Africa",
39622         "za",
39623         "27"
39624       ],
39625       [
39626         "South Korea (대한민국)",
39627         "kr",
39628         "82"
39629       ],
39630       [
39631         "South Sudan (‫جنوب السودان‬‎)",
39632         "ss",
39633         "211"
39634       ],
39635       [
39636         "Spain (España)",
39637         "es",
39638         "34"
39639       ],
39640       [
39641         "Sri Lanka (ශ්‍රී ලංකාව)",
39642         "lk",
39643         "94"
39644       ],
39645       [
39646         "Sudan (‫السودان‬‎)",
39647         "sd",
39648         "249"
39649       ],
39650       [
39651         "Suriname",
39652         "sr",
39653         "597"
39654       ],
39655       [
39656         "Svalbard and Jan Mayen",
39657         "sj",
39658         "47",
39659         1
39660       ],
39661       [
39662         "Swaziland",
39663         "sz",
39664         "268"
39665       ],
39666       [
39667         "Sweden (Sverige)",
39668         "se",
39669         "46"
39670       ],
39671       [
39672         "Switzerland (Schweiz)",
39673         "ch",
39674         "41"
39675       ],
39676       [
39677         "Syria (‫سوريا‬‎)",
39678         "sy",
39679         "963"
39680       ],
39681       [
39682         "Taiwan (台灣)",
39683         "tw",
39684         "886"
39685       ],
39686       [
39687         "Tajikistan",
39688         "tj",
39689         "992"
39690       ],
39691       [
39692         "Tanzania",
39693         "tz",
39694         "255"
39695       ],
39696       [
39697         "Thailand (ไทย)",
39698         "th",
39699         "66"
39700       ],
39701       [
39702         "Timor-Leste",
39703         "tl",
39704         "670"
39705       ],
39706       [
39707         "Togo",
39708         "tg",
39709         "228"
39710       ],
39711       [
39712         "Tokelau",
39713         "tk",
39714         "690"
39715       ],
39716       [
39717         "Tonga",
39718         "to",
39719         "676"
39720       ],
39721       [
39722         "Trinidad and Tobago",
39723         "tt",
39724         "1868"
39725       ],
39726       [
39727         "Tunisia (‫تونس‬‎)",
39728         "tn",
39729         "216"
39730       ],
39731       [
39732         "Turkey (Türkiye)",
39733         "tr",
39734         "90"
39735       ],
39736       [
39737         "Turkmenistan",
39738         "tm",
39739         "993"
39740       ],
39741       [
39742         "Turks and Caicos Islands",
39743         "tc",
39744         "1649"
39745       ],
39746       [
39747         "Tuvalu",
39748         "tv",
39749         "688"
39750       ],
39751       [
39752         "U.S. Virgin Islands",
39753         "vi",
39754         "1340"
39755       ],
39756       [
39757         "Uganda",
39758         "ug",
39759         "256"
39760       ],
39761       [
39762         "Ukraine (Україна)",
39763         "ua",
39764         "380"
39765       ],
39766       [
39767         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39768         "ae",
39769         "971"
39770       ],
39771       [
39772         "United Kingdom",
39773         "gb",
39774         "44",
39775         0
39776       ],
39777       [
39778         "United States",
39779         "us",
39780         "1",
39781         0
39782       ],
39783       [
39784         "Uruguay",
39785         "uy",
39786         "598"
39787       ],
39788       [
39789         "Uzbekistan (Oʻzbekiston)",
39790         "uz",
39791         "998"
39792       ],
39793       [
39794         "Vanuatu",
39795         "vu",
39796         "678"
39797       ],
39798       [
39799         "Vatican City (Città del Vaticano)",
39800         "va",
39801         "39",
39802         1
39803       ],
39804       [
39805         "Venezuela",
39806         "ve",
39807         "58"
39808       ],
39809       [
39810         "Vietnam (Việt Nam)",
39811         "vn",
39812         "84"
39813       ],
39814       [
39815         "Wallis and Futuna (Wallis-et-Futuna)",
39816         "wf",
39817         "681"
39818       ],
39819       [
39820         "Western Sahara (‫الصحراء الغربية‬‎)",
39821         "eh",
39822         "212",
39823         1
39824       ],
39825       [
39826         "Yemen (‫اليمن‬‎)",
39827         "ye",
39828         "967"
39829       ],
39830       [
39831         "Zambia",
39832         "zm",
39833         "260"
39834       ],
39835       [
39836         "Zimbabwe",
39837         "zw",
39838         "263"
39839       ],
39840       [
39841         "Åland Islands",
39842         "ax",
39843         "358",
39844         1
39845       ]
39846   ];
39847   
39848   return d;
39849 }/**
39850 *    This script refer to:
39851 *    Title: International Telephone Input
39852 *    Author: Jack O'Connor
39853 *    Code version:  v12.1.12
39854 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39855 **/
39856
39857 /**
39858  * @class Roo.bootstrap.PhoneInput
39859  * @extends Roo.bootstrap.TriggerField
39860  * An input with International dial-code selection
39861  
39862  * @cfg {String} defaultDialCode default '+852'
39863  * @cfg {Array} preferedCountries default []
39864   
39865  * @constructor
39866  * Create a new PhoneInput.
39867  * @param {Object} config Configuration options
39868  */
39869
39870 Roo.bootstrap.PhoneInput = function(config) {
39871     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39872 };
39873
39874 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39875         
39876         listWidth: undefined,
39877         
39878         selectedClass: 'active',
39879         
39880         invalidClass : "has-warning",
39881         
39882         validClass: 'has-success',
39883         
39884         allowed: '0123456789',
39885         
39886         /**
39887          * @cfg {String} defaultDialCode The default dial code when initializing the input
39888          */
39889         defaultDialCode: '+852',
39890         
39891         /**
39892          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39893          */
39894         preferedCountries: false,
39895         
39896         getAutoCreate : function()
39897         {
39898             var data = Roo.bootstrap.PhoneInputData();
39899             var align = this.labelAlign || this.parentLabelAlign();
39900             var id = Roo.id();
39901             
39902             this.allCountries = [];
39903             this.dialCodeMapping = [];
39904             
39905             for (var i = 0; i < data.length; i++) {
39906               var c = data[i];
39907               this.allCountries[i] = {
39908                 name: c[0],
39909                 iso2: c[1],
39910                 dialCode: c[2],
39911                 priority: c[3] || 0,
39912                 areaCodes: c[4] || null
39913               };
39914               this.dialCodeMapping[c[2]] = {
39915                   name: c[0],
39916                   iso2: c[1],
39917                   priority: c[3] || 0,
39918                   areaCodes: c[4] || null
39919               };
39920             }
39921             
39922             var cfg = {
39923                 cls: 'form-group',
39924                 cn: []
39925             };
39926             
39927             var input =  {
39928                 tag: 'input',
39929                 id : id,
39930                 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             
40144             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40145
40146             this.view = new Roo.View(this.list, this.tpl, {
40147                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40148             });
40149             
40150             this.view.on('click', this.onViewClick, this);
40151             this.setValue(this.defaultDialCode);
40152         },
40153         
40154         onTriggerClick : function(e)
40155         {
40156             Roo.log('trigger click');
40157             if(this.disabled){
40158                 return;
40159             }
40160             
40161             if(this.isExpanded()){
40162                 this.collapse();
40163                 this.hasFocus = false;
40164             }else {
40165                 this.store.load({});
40166                 this.hasFocus = true;
40167                 this.expand();
40168             }
40169         },
40170         
40171         isExpanded : function()
40172         {
40173             return this.list.isVisible();
40174         },
40175         
40176         collapse : function()
40177         {
40178             if(!this.isExpanded()){
40179                 return;
40180             }
40181             this.list.hide();
40182             Roo.get(document).un('mousedown', this.collapseIf, this);
40183             Roo.get(document).un('mousewheel', this.collapseIf, this);
40184             this.fireEvent('collapse', this);
40185             this.validate();
40186         },
40187         
40188         expand : function()
40189         {
40190             Roo.log('expand');
40191
40192             if(this.isExpanded() || !this.hasFocus){
40193                 return;
40194             }
40195             
40196             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40197             this.list.setWidth(lw);
40198             
40199             this.list.show();
40200             this.restrictHeight();
40201             
40202             Roo.get(document).on('mousedown', this.collapseIf, this);
40203             Roo.get(document).on('mousewheel', this.collapseIf, this);
40204             
40205             this.fireEvent('expand', this);
40206         },
40207         
40208         restrictHeight : function()
40209         {
40210             this.list.alignTo(this.inputEl(), this.listAlign);
40211             this.list.alignTo(this.inputEl(), this.listAlign);
40212         },
40213         
40214         onViewOver : function(e, t)
40215         {
40216             if(this.inKeyMode){
40217                 return;
40218             }
40219             var item = this.view.findItemFromChild(t);
40220             
40221             if(item){
40222                 var index = this.view.indexOf(item);
40223                 this.select(index, false);
40224             }
40225         },
40226
40227         // private
40228         onViewClick : function(view, doFocus, el, e)
40229         {
40230             var index = this.view.getSelectedIndexes()[0];
40231             
40232             var r = this.store.getAt(index);
40233             
40234             if(r){
40235                 this.onSelect(r, index);
40236             }
40237             if(doFocus !== false && !this.blockFocus){
40238                 this.inputEl().focus();
40239             }
40240         },
40241         
40242         onViewMove : function(e, t)
40243         {
40244             this.inKeyMode = false;
40245         },
40246         
40247         select : function(index, scrollIntoView)
40248         {
40249             this.selectedIndex = index;
40250             this.view.select(index);
40251             if(scrollIntoView !== false){
40252                 var el = this.view.getNode(index);
40253                 if(el){
40254                     this.list.scrollChildIntoView(el, false);
40255                 }
40256             }
40257         },
40258         
40259         createList : function()
40260         {
40261             this.list = Roo.get(document.body).createChild({
40262                 tag: 'ul',
40263                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40264                 style: 'display:none'
40265             });
40266             
40267             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40268         },
40269         
40270         collapseIf : function(e)
40271         {
40272             var in_combo  = e.within(this.el);
40273             var in_list =  e.within(this.list);
40274             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40275             
40276             if (in_combo || in_list || is_list) {
40277                 return;
40278             }
40279             this.collapse();
40280         },
40281         
40282         onSelect : function(record, index)
40283         {
40284             if(this.fireEvent('beforeselect', this, record, index) !== false){
40285                 
40286                 this.setFlagClass(record.data.iso2);
40287                 this.setDialCode(record.data.dialCode);
40288                 this.hasFocus = false;
40289                 this.collapse();
40290                 this.fireEvent('select', this, record, index);
40291             }
40292         },
40293         
40294         flagEl : function()
40295         {
40296             var flag = this.el.select('div.flag',true).first();
40297             if(!flag){
40298                 return false;
40299             }
40300             return flag;
40301         },
40302         
40303         dialCodeHolderEl : function()
40304         {
40305             var d = this.el.select('input.dial-code-holder',true).first();
40306             if(!d){
40307                 return false;
40308             }
40309             return d;
40310         },
40311         
40312         setDialCode : function(v)
40313         {
40314             this.dialCodeHolder.dom.value = '+'+v;
40315         },
40316         
40317         setFlagClass : function(n)
40318         {
40319             this.flag.dom.className = 'flag '+n;
40320         },
40321         
40322         getValue : function()
40323         {
40324             var v = this.inputEl().getValue();
40325             if(this.dialCodeHolder) {
40326                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40327             }
40328             return v;
40329         },
40330         
40331         setValue : function(v)
40332         {
40333             var d = this.getDialCode(v);
40334             
40335             //invalid dial code
40336             if(v.length == 0 || !d || d.length == 0) {
40337                 if(this.rendered){
40338                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40339                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40340                 }
40341                 return;
40342             }
40343             
40344             //valid dial code
40345             this.setFlagClass(this.dialCodeMapping[d].iso2);
40346             this.setDialCode(d);
40347             this.inputEl().dom.value = v.replace('+'+d,'');
40348             this.hiddenEl().dom.value = this.getValue();
40349             
40350             this.validate();
40351         },
40352         
40353         getDialCode : function(v)
40354         {
40355             v = v ||  '';
40356             
40357             if (v.length == 0) {
40358                 return this.dialCodeHolder.dom.value;
40359             }
40360             
40361             var dialCode = "";
40362             if (v.charAt(0) != "+") {
40363                 return false;
40364             }
40365             var numericChars = "";
40366             for (var i = 1; i < v.length; i++) {
40367               var c = v.charAt(i);
40368               if (!isNaN(c)) {
40369                 numericChars += c;
40370                 if (this.dialCodeMapping[numericChars]) {
40371                   dialCode = v.substr(1, i);
40372                 }
40373                 if (numericChars.length == 4) {
40374                   break;
40375                 }
40376               }
40377             }
40378             return dialCode;
40379         },
40380         
40381         reset : function()
40382         {
40383             this.setValue(this.defaultDialCode);
40384             this.validate();
40385         },
40386         
40387         hiddenEl : function()
40388         {
40389             return this.el.select('input.hidden-tel-input',true).first();
40390         },
40391         
40392         onKeyUp : function(e){
40393             
40394             var k = e.getKey();
40395             var c = e.getCharCode();
40396             
40397             if(
40398                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40399                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40400             ){
40401                 e.stopEvent();
40402             }
40403             
40404             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40405             //     return;
40406             // }
40407             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40408                 e.stopEvent();
40409             }
40410             
40411             this.setValue(this.getValue());
40412         }
40413         
40414 });
40415 /**
40416  * @class Roo.bootstrap.MoneyField
40417  * @extends Roo.bootstrap.ComboBox
40418  * Bootstrap MoneyField class
40419  * 
40420  * @constructor
40421  * Create a new MoneyField.
40422  * @param {Object} config Configuration options
40423  */
40424
40425 Roo.bootstrap.MoneyField = function(config) {
40426     
40427     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40428     
40429 };
40430
40431 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40432     
40433     /**
40434      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40435      */
40436     allowDecimals : true,
40437     /**
40438      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40439      */
40440     decimalSeparator : ".",
40441     /**
40442      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40443      */
40444     decimalPrecision : 0,
40445     /**
40446      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40447      */
40448     allowNegative : true,
40449     /**
40450      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40451      */
40452     allowZero: true,
40453     /**
40454      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40455      */
40456     minValue : Number.NEGATIVE_INFINITY,
40457     /**
40458      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40459      */
40460     maxValue : Number.MAX_VALUE,
40461     /**
40462      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40463      */
40464     minText : "The minimum value for this field is {0}",
40465     /**
40466      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40467      */
40468     maxText : "The maximum value for this field is {0}",
40469     /**
40470      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40471      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40472      */
40473     nanText : "{0} is not a valid number",
40474     /**
40475      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40476      */
40477     castInt : true,
40478     /**
40479      * @cfg {String} defaults currency of the MoneyField
40480      * value should be in lkey
40481      */
40482     defaultCurrency : false,
40483     /**
40484      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40485      */
40486     thousandsDelimiter : false,
40487     
40488     
40489     inputlg : 9,
40490     inputmd : 9,
40491     inputsm : 9,
40492     inputxs : 6,
40493     
40494     store : false,
40495     
40496     getAutoCreate : function()
40497     {
40498         var align = this.labelAlign || this.parentLabelAlign();
40499         
40500         var id = Roo.id();
40501
40502         var cfg = {
40503             cls: 'form-group',
40504             cn: []
40505         };
40506
40507         var input =  {
40508             tag: 'input',
40509             id : id,
40510             cls : 'form-control roo-money-amount-input',
40511             autocomplete: 'new-password'
40512         };
40513         
40514         var hiddenInput = {
40515             tag: 'input',
40516             type: 'hidden',
40517             id: Roo.id(),
40518             cls: 'hidden-number-input'
40519         };
40520         
40521         if (this.name) {
40522             hiddenInput.name = this.name;
40523         }
40524
40525         if (this.disabled) {
40526             input.disabled = true;
40527         }
40528
40529         var clg = 12 - this.inputlg;
40530         var cmd = 12 - this.inputmd;
40531         var csm = 12 - this.inputsm;
40532         var cxs = 12 - this.inputxs;
40533         
40534         var container = {
40535             tag : 'div',
40536             cls : 'row roo-money-field',
40537             cn : [
40538                 {
40539                     tag : 'div',
40540                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40541                     cn : [
40542                         {
40543                             tag : 'div',
40544                             cls: 'roo-select2-container input-group',
40545                             cn: [
40546                                 {
40547                                     tag : 'input',
40548                                     cls : 'form-control roo-money-currency-input',
40549                                     autocomplete: 'new-password',
40550                                     readOnly : 1,
40551                                     name : this.currencyName
40552                                 },
40553                                 {
40554                                     tag :'span',
40555                                     cls : 'input-group-addon',
40556                                     cn : [
40557                                         {
40558                                             tag: 'span',
40559                                             cls: 'caret'
40560                                         }
40561                                     ]
40562                                 }
40563                             ]
40564                         }
40565                     ]
40566                 },
40567                 {
40568                     tag : 'div',
40569                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40570                     cn : [
40571                         {
40572                             tag: 'div',
40573                             cls: this.hasFeedback ? 'has-feedback' : '',
40574                             cn: [
40575                                 input
40576                             ]
40577                         }
40578                     ]
40579                 }
40580             ]
40581             
40582         };
40583         
40584         if (this.fieldLabel.length) {
40585             var indicator = {
40586                 tag: 'i',
40587                 tooltip: 'This field is required'
40588             };
40589
40590             var label = {
40591                 tag: 'label',
40592                 'for':  id,
40593                 cls: 'control-label',
40594                 cn: []
40595             };
40596
40597             var label_text = {
40598                 tag: 'span',
40599                 html: this.fieldLabel
40600             };
40601
40602             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40603             label.cn = [
40604                 indicator,
40605                 label_text
40606             ];
40607
40608             if(this.indicatorpos == 'right') {
40609                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40610                 label.cn = [
40611                     label_text,
40612                     indicator
40613                 ];
40614             }
40615
40616             if(align == 'left') {
40617                 container = {
40618                     tag: 'div',
40619                     cn: [
40620                         container
40621                     ]
40622                 };
40623
40624                 if(this.labelWidth > 12){
40625                     label.style = "width: " + this.labelWidth + 'px';
40626                 }
40627                 if(this.labelWidth < 13 && this.labelmd == 0){
40628                     this.labelmd = this.labelWidth;
40629                 }
40630                 if(this.labellg > 0){
40631                     label.cls += ' col-lg-' + this.labellg;
40632                     input.cls += ' col-lg-' + (12 - this.labellg);
40633                 }
40634                 if(this.labelmd > 0){
40635                     label.cls += ' col-md-' + this.labelmd;
40636                     container.cls += ' col-md-' + (12 - this.labelmd);
40637                 }
40638                 if(this.labelsm > 0){
40639                     label.cls += ' col-sm-' + this.labelsm;
40640                     container.cls += ' col-sm-' + (12 - this.labelsm);
40641                 }
40642                 if(this.labelxs > 0){
40643                     label.cls += ' col-xs-' + this.labelxs;
40644                     container.cls += ' col-xs-' + (12 - this.labelxs);
40645                 }
40646             }
40647         }
40648
40649         cfg.cn = [
40650             label,
40651             container,
40652             hiddenInput
40653         ];
40654         
40655         var settings = this;
40656
40657         ['xs','sm','md','lg'].map(function(size){
40658             if (settings[size]) {
40659                 cfg.cls += ' col-' + size + '-' + settings[size];
40660             }
40661         });
40662         
40663         return cfg;
40664     },
40665     
40666     initEvents : function()
40667     {
40668         this.indicator = this.indicatorEl();
40669         
40670         this.initCurrencyEvent();
40671         
40672         this.initNumberEvent();
40673     },
40674     
40675     initCurrencyEvent : function()
40676     {
40677         if (!this.store) {
40678             throw "can not find store for combo";
40679         }
40680         
40681         this.store = Roo.factory(this.store, Roo.data);
40682         this.store.parent = this;
40683         
40684         this.createList();
40685         
40686         this.triggerEl = this.el.select('.input-group-addon', true).first();
40687         
40688         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40689         
40690         var _this = this;
40691         
40692         (function(){
40693             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40694             _this.list.setWidth(lw);
40695         }).defer(100);
40696         
40697         this.list.on('mouseover', this.onViewOver, this);
40698         this.list.on('mousemove', this.onViewMove, this);
40699         this.list.on('scroll', this.onViewScroll, this);
40700         
40701         if(!this.tpl){
40702             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40703         }
40704         
40705         this.view = new Roo.View(this.list, this.tpl, {
40706             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40707         });
40708         
40709         this.view.on('click', this.onViewClick, this);
40710         
40711         this.store.on('beforeload', this.onBeforeLoad, this);
40712         this.store.on('load', this.onLoad, this);
40713         this.store.on('loadexception', this.onLoadException, this);
40714         
40715         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40716             "up" : function(e){
40717                 this.inKeyMode = true;
40718                 this.selectPrev();
40719             },
40720
40721             "down" : function(e){
40722                 if(!this.isExpanded()){
40723                     this.onTriggerClick();
40724                 }else{
40725                     this.inKeyMode = true;
40726                     this.selectNext();
40727                 }
40728             },
40729
40730             "enter" : function(e){
40731                 this.collapse();
40732                 
40733                 if(this.fireEvent("specialkey", this, e)){
40734                     this.onViewClick(false);
40735                 }
40736                 
40737                 return true;
40738             },
40739
40740             "esc" : function(e){
40741                 this.collapse();
40742             },
40743
40744             "tab" : function(e){
40745                 this.collapse();
40746                 
40747                 if(this.fireEvent("specialkey", this, e)){
40748                     this.onViewClick(false);
40749                 }
40750                 
40751                 return true;
40752             },
40753
40754             scope : this,
40755
40756             doRelay : function(foo, bar, hname){
40757                 if(hname == 'down' || this.scope.isExpanded()){
40758                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40759                 }
40760                 return true;
40761             },
40762
40763             forceKeyDown: true
40764         });
40765         
40766         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40767         
40768     },
40769     
40770     initNumberEvent : function(e)
40771     {
40772         this.inputEl().on("keydown" , this.fireKey,  this);
40773         this.inputEl().on("focus", this.onFocus,  this);
40774         this.inputEl().on("blur", this.onBlur,  this);
40775         
40776         this.inputEl().relayEvent('keyup', this);
40777         
40778         if(this.indicator){
40779             this.indicator.addClass('invisible');
40780         }
40781  
40782         this.originalValue = this.getValue();
40783         
40784         if(this.validationEvent == 'keyup'){
40785             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40786             this.inputEl().on('keyup', this.filterValidation, this);
40787         }
40788         else if(this.validationEvent !== false){
40789             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40790         }
40791         
40792         if(this.selectOnFocus){
40793             this.on("focus", this.preFocus, this);
40794             
40795         }
40796         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40797             this.inputEl().on("keypress", this.filterKeys, this);
40798         } else {
40799             this.inputEl().relayEvent('keypress', this);
40800         }
40801         
40802         var allowed = "0123456789";
40803         
40804         if(this.allowDecimals){
40805             allowed += this.decimalSeparator;
40806         }
40807         
40808         if(this.allowNegative){
40809             allowed += "-";
40810         }
40811         
40812         if(this.thousandsDelimiter) {
40813             allowed += ",";
40814         }
40815         
40816         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40817         
40818         var keyPress = function(e){
40819             
40820             var k = e.getKey();
40821             
40822             var c = e.getCharCode();
40823             
40824             if(
40825                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40826                     allowed.indexOf(String.fromCharCode(c)) === -1
40827             ){
40828                 e.stopEvent();
40829                 return;
40830             }
40831             
40832             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40833                 return;
40834             }
40835             
40836             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40837                 e.stopEvent();
40838             }
40839         };
40840         
40841         this.inputEl().on("keypress", keyPress, this);
40842         
40843     },
40844     
40845     onTriggerClick : function(e)
40846     {   
40847         if(this.disabled){
40848             return;
40849         }
40850         
40851         this.page = 0;
40852         this.loadNext = false;
40853         
40854         if(this.isExpanded()){
40855             this.collapse();
40856             return;
40857         }
40858         
40859         this.hasFocus = true;
40860         
40861         if(this.triggerAction == 'all') {
40862             this.doQuery(this.allQuery, true);
40863             return;
40864         }
40865         
40866         this.doQuery(this.getRawValue());
40867     },
40868     
40869     getCurrency : function()
40870     {   
40871         var v = this.currencyEl().getValue();
40872         
40873         return v;
40874     },
40875     
40876     restrictHeight : function()
40877     {
40878         this.list.alignTo(this.currencyEl(), this.listAlign);
40879         this.list.alignTo(this.currencyEl(), this.listAlign);
40880     },
40881     
40882     onViewClick : function(view, doFocus, el, e)
40883     {
40884         var index = this.view.getSelectedIndexes()[0];
40885         
40886         var r = this.store.getAt(index);
40887         
40888         if(r){
40889             this.onSelect(r, index);
40890         }
40891     },
40892     
40893     onSelect : function(record, index){
40894         
40895         if(this.fireEvent('beforeselect', this, record, index) !== false){
40896         
40897             this.setFromCurrencyData(index > -1 ? record.data : false);
40898             
40899             this.collapse();
40900             
40901             this.fireEvent('select', this, record, index);
40902         }
40903     },
40904     
40905     setFromCurrencyData : function(o)
40906     {
40907         var currency = '';
40908         
40909         this.lastCurrency = o;
40910         
40911         if (this.currencyField) {
40912             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40913         } else {
40914             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40915         }
40916         
40917         this.lastSelectionText = currency;
40918         
40919         //setting default currency
40920         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40921             this.setCurrency(this.defaultCurrency);
40922             return;
40923         }
40924         
40925         this.setCurrency(currency);
40926     },
40927     
40928     setFromData : function(o)
40929     {
40930         var c = {};
40931         
40932         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40933         
40934         this.setFromCurrencyData(c);
40935         
40936         var value = '';
40937         
40938         if (this.name) {
40939             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40940         } else {
40941             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40942         }
40943         
40944         this.setValue(value);
40945         
40946     },
40947     
40948     setCurrency : function(v)
40949     {   
40950         this.currencyValue = v;
40951         
40952         if(this.rendered){
40953             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40954             this.validate();
40955         }
40956     },
40957     
40958     setValue : function(v)
40959     {
40960         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40961         
40962         this.value = v;
40963         
40964         if(this.rendered){
40965             
40966             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40967             
40968             this.inputEl().dom.value = (v == '') ? '' :
40969                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40970             
40971             if(!this.allowZero && v === '0') {
40972                 this.hiddenEl().dom.value = '';
40973                 this.inputEl().dom.value = '';
40974             }
40975             
40976             this.validate();
40977         }
40978     },
40979     
40980     getRawValue : function()
40981     {
40982         var v = this.inputEl().getValue();
40983         
40984         return v;
40985     },
40986     
40987     getValue : function()
40988     {
40989         return this.fixPrecision(this.parseValue(this.getRawValue()));
40990     },
40991     
40992     parseValue : function(value)
40993     {
40994         if(this.thousandsDelimiter) {
40995             value += "";
40996             r = new RegExp(",", "g");
40997             value = value.replace(r, "");
40998         }
40999         
41000         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41001         return isNaN(value) ? '' : value;
41002         
41003     },
41004     
41005     fixPrecision : function(value)
41006     {
41007         if(this.thousandsDelimiter) {
41008             value += "";
41009             r = new RegExp(",", "g");
41010             value = value.replace(r, "");
41011         }
41012         
41013         var nan = isNaN(value);
41014         
41015         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41016             return nan ? '' : value;
41017         }
41018         return parseFloat(value).toFixed(this.decimalPrecision);
41019     },
41020     
41021     decimalPrecisionFcn : function(v)
41022     {
41023         return Math.floor(v);
41024     },
41025     
41026     validateValue : function(value)
41027     {
41028         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41029             return false;
41030         }
41031         
41032         var num = this.parseValue(value);
41033         
41034         if(isNaN(num)){
41035             this.markInvalid(String.format(this.nanText, value));
41036             return false;
41037         }
41038         
41039         if(num < this.minValue){
41040             this.markInvalid(String.format(this.minText, this.minValue));
41041             return false;
41042         }
41043         
41044         if(num > this.maxValue){
41045             this.markInvalid(String.format(this.maxText, this.maxValue));
41046             return false;
41047         }
41048         
41049         return true;
41050     },
41051     
41052     validate : function()
41053     {
41054         if(this.disabled || this.allowBlank){
41055             this.markValid();
41056             return true;
41057         }
41058         
41059         var currency = this.getCurrency();
41060         
41061         if(this.validateValue(this.getRawValue()) && currency.length){
41062             this.markValid();
41063             return true;
41064         }
41065         
41066         this.markInvalid();
41067         return false;
41068     },
41069     
41070     getName: function()
41071     {
41072         return this.name;
41073     },
41074     
41075     beforeBlur : function()
41076     {
41077         if(!this.castInt){
41078             return;
41079         }
41080         
41081         var v = this.parseValue(this.getRawValue());
41082         
41083         if(v || v == 0){
41084             this.setValue(v);
41085         }
41086     },
41087     
41088     onBlur : function()
41089     {
41090         this.beforeBlur();
41091         
41092         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41093             //this.el.removeClass(this.focusClass);
41094         }
41095         
41096         this.hasFocus = false;
41097         
41098         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41099             this.validate();
41100         }
41101         
41102         var v = this.getValue();
41103         
41104         if(String(v) !== String(this.startValue)){
41105             this.fireEvent('change', this, v, this.startValue);
41106         }
41107         
41108         this.fireEvent("blur", this);
41109     },
41110     
41111     inputEl : function()
41112     {
41113         return this.el.select('.roo-money-amount-input', true).first();
41114     },
41115     
41116     currencyEl : function()
41117     {
41118         return this.el.select('.roo-money-currency-input', true).first();
41119     },
41120     
41121     hiddenEl : function()
41122     {
41123         return this.el.select('input.hidden-number-input',true).first();
41124     }
41125     
41126 });